diff --git a/Documentation/ABI/testing/sysfs-block b/Documentation/ABI/testing/sysfs-block index 279da08f754192c07ba10c47eb0a4ea8e8c95892..8df003963d99c0500eb9a1c3ee20be367812eb36 100644 --- a/Documentation/ABI/testing/sysfs-block +++ b/Documentation/ABI/testing/sysfs-block @@ -53,6 +53,14 @@ Description: 512 bytes of data. +What: /sys/block//integrity/device_is_integrity_capable +Date: July 2014 +Contact: Martin K. Petersen +Description: + Indicates whether a storage device is capable of storing + integrity metadata. Set if the device is T10 PI-capable. + + What: /sys/block//integrity/write_generate Date: June 2008 Contact: Martin K. Petersen diff --git a/Documentation/ABI/testing/sysfs-bus-event_source-devices-events b/Documentation/ABI/testing/sysfs-bus-event_source-devices-events index 7b40a3cbc26a8026cd818535d1e27f51261353e2..20979f8b3edbc48645e4e274772b4ebd26987962 100644 --- a/Documentation/ABI/testing/sysfs-bus-event_source-devices-events +++ b/Documentation/ABI/testing/sysfs-bus-event_source-devices-events @@ -27,575 +27,62 @@ Description: Generic performance monitoring events "basename". -What: /sys/devices/cpu/events/PM_1PLUS_PPC_CMPL - /sys/devices/cpu/events/PM_BRU_FIN - /sys/devices/cpu/events/PM_BR_MPRED - /sys/devices/cpu/events/PM_CMPLU_STALL - /sys/devices/cpu/events/PM_CMPLU_STALL_BRU - /sys/devices/cpu/events/PM_CMPLU_STALL_DCACHE_MISS - /sys/devices/cpu/events/PM_CMPLU_STALL_DFU - /sys/devices/cpu/events/PM_CMPLU_STALL_DIV - /sys/devices/cpu/events/PM_CMPLU_STALL_ERAT_MISS - /sys/devices/cpu/events/PM_CMPLU_STALL_FXU - /sys/devices/cpu/events/PM_CMPLU_STALL_IFU - /sys/devices/cpu/events/PM_CMPLU_STALL_LSU - /sys/devices/cpu/events/PM_CMPLU_STALL_REJECT - /sys/devices/cpu/events/PM_CMPLU_STALL_SCALAR - /sys/devices/cpu/events/PM_CMPLU_STALL_SCALAR_LONG - /sys/devices/cpu/events/PM_CMPLU_STALL_STORE - /sys/devices/cpu/events/PM_CMPLU_STALL_THRD - /sys/devices/cpu/events/PM_CMPLU_STALL_VECTOR - /sys/devices/cpu/events/PM_CMPLU_STALL_VECTOR_LONG - /sys/devices/cpu/events/PM_CYC - /sys/devices/cpu/events/PM_GCT_NOSLOT_BR_MPRED - /sys/devices/cpu/events/PM_GCT_NOSLOT_BR_MPRED_IC_MISS - /sys/devices/cpu/events/PM_GCT_NOSLOT_CYC - /sys/devices/cpu/events/PM_GCT_NOSLOT_IC_MISS - /sys/devices/cpu/events/PM_GRP_CMPL - /sys/devices/cpu/events/PM_INST_CMPL - /sys/devices/cpu/events/PM_LD_MISS_L1 - /sys/devices/cpu/events/PM_LD_REF_L1 - /sys/devices/cpu/events/PM_RUN_CYC - /sys/devices/cpu/events/PM_RUN_INST_CMPL - /sys/devices/cpu/events/PM_IC_DEMAND_L2_BR_ALL - /sys/devices/cpu/events/PM_GCT_UTIL_7_TO_10_SLOTS - /sys/devices/cpu/events/PM_PMC2_SAVED - /sys/devices/cpu/events/PM_VSU0_16FLOP - /sys/devices/cpu/events/PM_MRK_LSU_DERAT_MISS - /sys/devices/cpu/events/PM_MRK_ST_CMPL - /sys/devices/cpu/events/PM_NEST_PAIR3_ADD - /sys/devices/cpu/events/PM_L2_ST_DISP - /sys/devices/cpu/events/PM_L2_CASTOUT_MOD - /sys/devices/cpu/events/PM_ISEG - /sys/devices/cpu/events/PM_MRK_INST_TIMEO - /sys/devices/cpu/events/PM_L2_RCST_DISP_FAIL_ADDR - /sys/devices/cpu/events/PM_LSU1_DC_PREF_STREAM_CONFIRM - /sys/devices/cpu/events/PM_IERAT_WR_64K - /sys/devices/cpu/events/PM_MRK_DTLB_MISS_16M - /sys/devices/cpu/events/PM_IERAT_MISS - /sys/devices/cpu/events/PM_MRK_PTEG_FROM_LMEM - /sys/devices/cpu/events/PM_FLOP - /sys/devices/cpu/events/PM_THRD_PRIO_4_5_CYC - /sys/devices/cpu/events/PM_BR_PRED_TA - /sys/devices/cpu/events/PM_EXT_INT - /sys/devices/cpu/events/PM_VSU_FSQRT_FDIV - /sys/devices/cpu/events/PM_MRK_LD_MISS_EXPOSED_CYC - /sys/devices/cpu/events/PM_LSU1_LDF - /sys/devices/cpu/events/PM_IC_WRITE_ALL - /sys/devices/cpu/events/PM_LSU0_SRQ_STFWD - /sys/devices/cpu/events/PM_PTEG_FROM_RL2L3_MOD - /sys/devices/cpu/events/PM_MRK_DATA_FROM_L31_SHR - /sys/devices/cpu/events/PM_DATA_FROM_L21_MOD - /sys/devices/cpu/events/PM_VSU1_SCAL_DOUBLE_ISSUED - /sys/devices/cpu/events/PM_VSU0_8FLOP - /sys/devices/cpu/events/PM_POWER_EVENT1 - /sys/devices/cpu/events/PM_DISP_CLB_HELD_BAL - /sys/devices/cpu/events/PM_VSU1_2FLOP - /sys/devices/cpu/events/PM_LWSYNC_HELD - /sys/devices/cpu/events/PM_PTEG_FROM_DL2L3_SHR - /sys/devices/cpu/events/PM_INST_FROM_L21_MOD - /sys/devices/cpu/events/PM_IERAT_XLATE_WR_16MPLUS - /sys/devices/cpu/events/PM_IC_REQ_ALL - /sys/devices/cpu/events/PM_DSLB_MISS - /sys/devices/cpu/events/PM_L3_MISS - /sys/devices/cpu/events/PM_LSU0_L1_PREF - /sys/devices/cpu/events/PM_VSU_SCALAR_SINGLE_ISSUED - /sys/devices/cpu/events/PM_LSU1_DC_PREF_STREAM_CONFIRM_STRIDE - /sys/devices/cpu/events/PM_L2_INST - /sys/devices/cpu/events/PM_VSU0_FRSP - /sys/devices/cpu/events/PM_FLUSH_DISP - /sys/devices/cpu/events/PM_PTEG_FROM_L2MISS - /sys/devices/cpu/events/PM_VSU1_DQ_ISSUED - /sys/devices/cpu/events/PM_MRK_DATA_FROM_DMEM - /sys/devices/cpu/events/PM_LSU_FLUSH_ULD - /sys/devices/cpu/events/PM_PTEG_FROM_LMEM - /sys/devices/cpu/events/PM_MRK_DERAT_MISS_16M - /sys/devices/cpu/events/PM_THRD_ALL_RUN_CYC - /sys/devices/cpu/events/PM_MEM0_PREFETCH_DISP - /sys/devices/cpu/events/PM_MRK_STALL_CMPLU_CYC_COUNT - /sys/devices/cpu/events/PM_DATA_FROM_DL2L3_MOD - /sys/devices/cpu/events/PM_VSU_FRSP - /sys/devices/cpu/events/PM_MRK_DATA_FROM_L21_MOD - /sys/devices/cpu/events/PM_PMC1_OVERFLOW - /sys/devices/cpu/events/PM_VSU0_SINGLE - /sys/devices/cpu/events/PM_MRK_PTEG_FROM_L3MISS - /sys/devices/cpu/events/PM_MRK_PTEG_FROM_L31_SHR - /sys/devices/cpu/events/PM_VSU0_VECTOR_SP_ISSUED - /sys/devices/cpu/events/PM_VSU1_FEST - /sys/devices/cpu/events/PM_MRK_INST_DISP - /sys/devices/cpu/events/PM_VSU0_COMPLEX_ISSUED - /sys/devices/cpu/events/PM_LSU1_FLUSH_UST - /sys/devices/cpu/events/PM_FXU_IDLE - /sys/devices/cpu/events/PM_LSU0_FLUSH_ULD - /sys/devices/cpu/events/PM_MRK_DATA_FROM_DL2L3_MOD - /sys/devices/cpu/events/PM_LSU_LMQ_SRQ_EMPTY_ALL_CYC - /sys/devices/cpu/events/PM_LSU1_REJECT_LMQ_FULL - /sys/devices/cpu/events/PM_INST_PTEG_FROM_L21_MOD - /sys/devices/cpu/events/PM_INST_FROM_RL2L3_MOD - /sys/devices/cpu/events/PM_SHL_CREATED - /sys/devices/cpu/events/PM_L2_ST_HIT - /sys/devices/cpu/events/PM_DATA_FROM_DMEM - /sys/devices/cpu/events/PM_L3_LD_MISS - /sys/devices/cpu/events/PM_FXU1_BUSY_FXU0_IDLE - /sys/devices/cpu/events/PM_DISP_CLB_HELD_RES - /sys/devices/cpu/events/PM_L2_SN_SX_I_DONE - /sys/devices/cpu/events/PM_STCX_CMPL - /sys/devices/cpu/events/PM_VSU0_2FLOP - /sys/devices/cpu/events/PM_L3_PREF_MISS - /sys/devices/cpu/events/PM_LSU_SRQ_SYNC_CYC - /sys/devices/cpu/events/PM_LSU_REJECT_ERAT_MISS - /sys/devices/cpu/events/PM_L1_ICACHE_MISS - /sys/devices/cpu/events/PM_LSU1_FLUSH_SRQ - /sys/devices/cpu/events/PM_LD_REF_L1_LSU0 - /sys/devices/cpu/events/PM_VSU0_FEST - /sys/devices/cpu/events/PM_VSU_VECTOR_SINGLE_ISSUED - /sys/devices/cpu/events/PM_FREQ_UP - /sys/devices/cpu/events/PM_DATA_FROM_LMEM - /sys/devices/cpu/events/PM_LSU1_LDX - /sys/devices/cpu/events/PM_PMC3_OVERFLOW - /sys/devices/cpu/events/PM_MRK_BR_MPRED - /sys/devices/cpu/events/PM_SHL_MATCH - /sys/devices/cpu/events/PM_MRK_BR_TAKEN - /sys/devices/cpu/events/PM_ISLB_MISS - /sys/devices/cpu/events/PM_DISP_HELD_THERMAL - /sys/devices/cpu/events/PM_INST_PTEG_FROM_RL2L3_SHR - /sys/devices/cpu/events/PM_LSU1_SRQ_STFWD - /sys/devices/cpu/events/PM_PTEG_FROM_DMEM - /sys/devices/cpu/events/PM_VSU_2FLOP - /sys/devices/cpu/events/PM_GCT_FULL_CYC - /sys/devices/cpu/events/PM_MRK_DATA_FROM_L3_CYC - /sys/devices/cpu/events/PM_LSU_SRQ_S0_ALLOC - /sys/devices/cpu/events/PM_MRK_DERAT_MISS_4K - /sys/devices/cpu/events/PM_BR_MPRED_TA - /sys/devices/cpu/events/PM_INST_PTEG_FROM_L2MISS - /sys/devices/cpu/events/PM_DPU_HELD_POWER - /sys/devices/cpu/events/PM_MRK_VSU_FIN - /sys/devices/cpu/events/PM_LSU_SRQ_S0_VALID - /sys/devices/cpu/events/PM_GCT_EMPTY_CYC - /sys/devices/cpu/events/PM_IOPS_DISP - /sys/devices/cpu/events/PM_RUN_SPURR - /sys/devices/cpu/events/PM_PTEG_FROM_L21_MOD - /sys/devices/cpu/events/PM_VSU0_1FLOP - /sys/devices/cpu/events/PM_SNOOP_TLBIE - /sys/devices/cpu/events/PM_DATA_FROM_L3MISS - /sys/devices/cpu/events/PM_VSU_SINGLE - /sys/devices/cpu/events/PM_DTLB_MISS_16G - /sys/devices/cpu/events/PM_FLUSH - /sys/devices/cpu/events/PM_L2_LD_HIT - /sys/devices/cpu/events/PM_NEST_PAIR2_AND - /sys/devices/cpu/events/PM_VSU1_1FLOP - /sys/devices/cpu/events/PM_IC_PREF_REQ - /sys/devices/cpu/events/PM_L3_LD_HIT - /sys/devices/cpu/events/PM_DISP_HELD - /sys/devices/cpu/events/PM_L2_LD - /sys/devices/cpu/events/PM_LSU_FLUSH_SRQ - /sys/devices/cpu/events/PM_BC_PLUS_8_CONV - /sys/devices/cpu/events/PM_MRK_DATA_FROM_L31_MOD_CYC - /sys/devices/cpu/events/PM_L2_RCST_BUSY_RC_FULL - /sys/devices/cpu/events/PM_TB_BIT_TRANS - /sys/devices/cpu/events/PM_THERMAL_MAX - /sys/devices/cpu/events/PM_LSU1_FLUSH_ULD - /sys/devices/cpu/events/PM_LSU1_REJECT_LHS - /sys/devices/cpu/events/PM_LSU_LRQ_S0_ALLOC - /sys/devices/cpu/events/PM_L3_CO_L31 - /sys/devices/cpu/events/PM_POWER_EVENT4 - /sys/devices/cpu/events/PM_DATA_FROM_L31_SHR - /sys/devices/cpu/events/PM_BR_UNCOND - /sys/devices/cpu/events/PM_LSU1_DC_PREF_STREAM_ALLOC - /sys/devices/cpu/events/PM_PMC4_REWIND - /sys/devices/cpu/events/PM_L2_RCLD_DISP - /sys/devices/cpu/events/PM_THRD_PRIO_2_3_CYC - /sys/devices/cpu/events/PM_MRK_PTEG_FROM_L2MISS - /sys/devices/cpu/events/PM_IC_DEMAND_L2_BHT_REDIRECT - /sys/devices/cpu/events/PM_DATA_FROM_L31_SHR - /sys/devices/cpu/events/PM_IC_PREF_CANCEL_L2 - /sys/devices/cpu/events/PM_MRK_FIN_STALL_CYC_COUNT - /sys/devices/cpu/events/PM_BR_PRED_CCACHE - /sys/devices/cpu/events/PM_GCT_UTIL_1_TO_2_SLOTS - /sys/devices/cpu/events/PM_MRK_ST_CMPL_INT - /sys/devices/cpu/events/PM_LSU_TWO_TABLEWALK_CYC - /sys/devices/cpu/events/PM_MRK_DATA_FROM_L3MISS - /sys/devices/cpu/events/PM_LSU_SET_MPRED - /sys/devices/cpu/events/PM_FLUSH_DISP_TLBIE - /sys/devices/cpu/events/PM_VSU1_FCONV - /sys/devices/cpu/events/PM_DERAT_MISS_16G - /sys/devices/cpu/events/PM_INST_FROM_LMEM - /sys/devices/cpu/events/PM_IC_DEMAND_L2_BR_REDIRECT - /sys/devices/cpu/events/PM_INST_PTEG_FROM_L2 - /sys/devices/cpu/events/PM_PTEG_FROM_L2 - /sys/devices/cpu/events/PM_MRK_DATA_FROM_L21_SHR_CYC - /sys/devices/cpu/events/PM_MRK_DTLB_MISS_4K - /sys/devices/cpu/events/PM_VSU0_FPSCR - /sys/devices/cpu/events/PM_VSU1_VECT_DOUBLE_ISSUED - /sys/devices/cpu/events/PM_MRK_PTEG_FROM_RL2L3_MOD - /sys/devices/cpu/events/PM_MEM0_RQ_DISP - /sys/devices/cpu/events/PM_L2_LD_MISS - /sys/devices/cpu/events/PM_VMX_RESULT_SAT_1 - /sys/devices/cpu/events/PM_L1_PREF - /sys/devices/cpu/events/PM_MRK_DATA_FROM_LMEM_CYC - /sys/devices/cpu/events/PM_GRP_IC_MISS_NONSPEC - /sys/devices/cpu/events/PM_PB_NODE_PUMP - /sys/devices/cpu/events/PM_SHL_MERGED - /sys/devices/cpu/events/PM_NEST_PAIR1_ADD - /sys/devices/cpu/events/PM_DATA_FROM_L3 - /sys/devices/cpu/events/PM_LSU_FLUSH - /sys/devices/cpu/events/PM_LSU_SRQ_SYNC_COUNT - /sys/devices/cpu/events/PM_PMC2_OVERFLOW - /sys/devices/cpu/events/PM_LSU_LDF - /sys/devices/cpu/events/PM_POWER_EVENT3 - /sys/devices/cpu/events/PM_DISP_WT - /sys/devices/cpu/events/PM_IC_BANK_CONFLICT - /sys/devices/cpu/events/PM_BR_MPRED_CR_TA - /sys/devices/cpu/events/PM_L2_INST_MISS - /sys/devices/cpu/events/PM_NEST_PAIR2_ADD - /sys/devices/cpu/events/PM_MRK_LSU_FLUSH - /sys/devices/cpu/events/PM_L2_LDST - /sys/devices/cpu/events/PM_INST_FROM_L31_SHR - /sys/devices/cpu/events/PM_VSU0_FIN - /sys/devices/cpu/events/PM_VSU1_FCONV - /sys/devices/cpu/events/PM_INST_FROM_RMEM - /sys/devices/cpu/events/PM_DISP_CLB_HELD_TLBIE - /sys/devices/cpu/events/PM_MRK_DATA_FROM_DMEM_CYC - /sys/devices/cpu/events/PM_BR_PRED_CR - /sys/devices/cpu/events/PM_LSU_REJECT - /sys/devices/cpu/events/PM_GCT_UTIL_3_TO_6_SLOTS - /sys/devices/cpu/events/PM_CMPLU_STALL_END_GCT_NOSLOT - /sys/devices/cpu/events/PM_LSU0_REJECT_LMQ_FULL - /sys/devices/cpu/events/PM_VSU_FEST - /sys/devices/cpu/events/PM_NEST_PAIR0_AND - /sys/devices/cpu/events/PM_PTEG_FROM_L3 - /sys/devices/cpu/events/PM_POWER_EVENT2 - /sys/devices/cpu/events/PM_IC_PREF_CANCEL_PAGE - /sys/devices/cpu/events/PM_VSU0_FSQRT_FDIV - /sys/devices/cpu/events/PM_MRK_GRP_CMPL - /sys/devices/cpu/events/PM_VSU0_SCAL_DOUBLE_ISSUED - /sys/devices/cpu/events/PM_GRP_DISP - /sys/devices/cpu/events/PM_LSU0_LDX - /sys/devices/cpu/events/PM_DATA_FROM_L2 - /sys/devices/cpu/events/PM_MRK_DATA_FROM_RL2L3_MOD - /sys/devices/cpu/events/PM_VSU0_VECT_DOUBLE_ISSUED - /sys/devices/cpu/events/PM_VSU1_2FLOP_DOUBLE - /sys/devices/cpu/events/PM_THRD_PRIO_6_7_CYC - /sys/devices/cpu/events/PM_BC_PLUS_8_RSLV_TAKEN - /sys/devices/cpu/events/PM_BR_MPRED_CR - /sys/devices/cpu/events/PM_L3_CO_MEM - /sys/devices/cpu/events/PM_DATA_FROM_RL2L3_MOD - /sys/devices/cpu/events/PM_LSU_SRQ_FULL_CYC - /sys/devices/cpu/events/PM_TABLEWALK_CYC - /sys/devices/cpu/events/PM_MRK_PTEG_FROM_RMEM - /sys/devices/cpu/events/PM_LSU_SRQ_STFWD - /sys/devices/cpu/events/PM_INST_PTEG_FROM_RMEM - /sys/devices/cpu/events/PM_FXU0_FIN - /sys/devices/cpu/events/PM_LSU1_L1_SW_PREF - /sys/devices/cpu/events/PM_PTEG_FROM_L31_MOD - /sys/devices/cpu/events/PM_PMC5_OVERFLOW - /sys/devices/cpu/events/PM_LD_REF_L1_LSU1 - /sys/devices/cpu/events/PM_INST_PTEG_FROM_L21_SHR - /sys/devices/cpu/events/PM_DATA_FROM_RMEM - /sys/devices/cpu/events/PM_VSU0_SCAL_SINGLE_ISSUED - /sys/devices/cpu/events/PM_BR_MPRED_LSTACK - /sys/devices/cpu/events/PM_MRK_DATA_FROM_RL2L3_MOD_CYC - /sys/devices/cpu/events/PM_LSU0_FLUSH_UST - /sys/devices/cpu/events/PM_LSU_NCST - /sys/devices/cpu/events/PM_BR_TAKEN - /sys/devices/cpu/events/PM_INST_PTEG_FROM_LMEM - /sys/devices/cpu/events/PM_DTLB_MISS_4K - /sys/devices/cpu/events/PM_PMC4_SAVED - /sys/devices/cpu/events/PM_VSU1_PERMUTE_ISSUED - /sys/devices/cpu/events/PM_SLB_MISS - /sys/devices/cpu/events/PM_LSU1_FLUSH_LRQ - /sys/devices/cpu/events/PM_DTLB_MISS - /sys/devices/cpu/events/PM_VSU1_FRSP - /sys/devices/cpu/events/PM_VSU_VECTOR_DOUBLE_ISSUED - /sys/devices/cpu/events/PM_L2_CASTOUT_SHR - /sys/devices/cpu/events/PM_DATA_FROM_DL2L3_SHR - /sys/devices/cpu/events/PM_VSU1_STF - /sys/devices/cpu/events/PM_ST_FIN - /sys/devices/cpu/events/PM_PTEG_FROM_L21_SHR - /sys/devices/cpu/events/PM_L2_LOC_GUESS_WRONG - /sys/devices/cpu/events/PM_MRK_STCX_FAIL - /sys/devices/cpu/events/PM_LSU0_REJECT_LHS - /sys/devices/cpu/events/PM_IC_PREF_CANCEL_HIT - /sys/devices/cpu/events/PM_L3_PREF_BUSY - /sys/devices/cpu/events/PM_MRK_BRU_FIN - /sys/devices/cpu/events/PM_LSU1_NCLD - /sys/devices/cpu/events/PM_INST_PTEG_FROM_L31_MOD - /sys/devices/cpu/events/PM_LSU_NCLD - /sys/devices/cpu/events/PM_LSU_LDX - /sys/devices/cpu/events/PM_L2_LOC_GUESS_CORRECT - /sys/devices/cpu/events/PM_THRESH_TIMEO - /sys/devices/cpu/events/PM_L3_PREF_ST - /sys/devices/cpu/events/PM_DISP_CLB_HELD_SYNC - /sys/devices/cpu/events/PM_VSU_SIMPLE_ISSUED - /sys/devices/cpu/events/PM_VSU1_SINGLE - /sys/devices/cpu/events/PM_DATA_TABLEWALK_CYC - /sys/devices/cpu/events/PM_L2_RC_ST_DONE - /sys/devices/cpu/events/PM_MRK_PTEG_FROM_L21_MOD - /sys/devices/cpu/events/PM_LARX_LSU1 - /sys/devices/cpu/events/PM_MRK_DATA_FROM_RMEM - /sys/devices/cpu/events/PM_DISP_CLB_HELD - /sys/devices/cpu/events/PM_DERAT_MISS_4K - /sys/devices/cpu/events/PM_L2_RCLD_DISP_FAIL_ADDR - /sys/devices/cpu/events/PM_SEG_EXCEPTION - /sys/devices/cpu/events/PM_FLUSH_DISP_SB - /sys/devices/cpu/events/PM_L2_DC_INV - /sys/devices/cpu/events/PM_PTEG_FROM_DL2L3_MOD - /sys/devices/cpu/events/PM_DSEG - /sys/devices/cpu/events/PM_BR_PRED_LSTACK - /sys/devices/cpu/events/PM_VSU0_STF - /sys/devices/cpu/events/PM_LSU_FX_FIN - /sys/devices/cpu/events/PM_DERAT_MISS_16M - /sys/devices/cpu/events/PM_MRK_PTEG_FROM_DL2L3_MOD - /sys/devices/cpu/events/PM_GCT_UTIL_11_PLUS_SLOTS - /sys/devices/cpu/events/PM_INST_FROM_L3 - /sys/devices/cpu/events/PM_MRK_IFU_FIN - /sys/devices/cpu/events/PM_ITLB_MISS - /sys/devices/cpu/events/PM_VSU_STF - /sys/devices/cpu/events/PM_LSU_FLUSH_UST - /sys/devices/cpu/events/PM_L2_LDST_MISS - /sys/devices/cpu/events/PM_FXU1_FIN - /sys/devices/cpu/events/PM_SHL_DEALLOCATED - /sys/devices/cpu/events/PM_L2_SN_M_WR_DONE - /sys/devices/cpu/events/PM_LSU_REJECT_SET_MPRED - /sys/devices/cpu/events/PM_L3_PREF_LD - /sys/devices/cpu/events/PM_L2_SN_M_RD_DONE - /sys/devices/cpu/events/PM_MRK_DERAT_MISS_16G - /sys/devices/cpu/events/PM_VSU_FCONV - /sys/devices/cpu/events/PM_ANY_THRD_RUN_CYC - /sys/devices/cpu/events/PM_LSU_LMQ_FULL_CYC - /sys/devices/cpu/events/PM_MRK_LSU_REJECT_LHS - /sys/devices/cpu/events/PM_MRK_LD_MISS_L1_CYC - /sys/devices/cpu/events/PM_MRK_DATA_FROM_L2_CYC - /sys/devices/cpu/events/PM_INST_IMC_MATCH_DISP - /sys/devices/cpu/events/PM_MRK_DATA_FROM_RMEM_CYC - /sys/devices/cpu/events/PM_VSU0_SIMPLE_ISSUED - /sys/devices/cpu/events/PM_MRK_PTEG_FROM_RL2L3_SHR - /sys/devices/cpu/events/PM_VSU_FMA_DOUBLE - /sys/devices/cpu/events/PM_VSU_4FLOP - /sys/devices/cpu/events/PM_VSU1_FIN - /sys/devices/cpu/events/PM_NEST_PAIR1_AND - /sys/devices/cpu/events/PM_INST_PTEG_FROM_RL2L3_MOD - /sys/devices/cpu/events/PM_PTEG_FROM_RMEM - /sys/devices/cpu/events/PM_LSU_LRQ_S0_VALID - /sys/devices/cpu/events/PM_LSU0_LDF - /sys/devices/cpu/events/PM_FLUSH_COMPLETION - /sys/devices/cpu/events/PM_ST_MISS_L1 - /sys/devices/cpu/events/PM_L2_NODE_PUMP - /sys/devices/cpu/events/PM_INST_FROM_DL2L3_SHR - /sys/devices/cpu/events/PM_MRK_STALL_CMPLU_CYC - /sys/devices/cpu/events/PM_VSU1_DENORM - /sys/devices/cpu/events/PM_MRK_DATA_FROM_L31_SHR_CYC - /sys/devices/cpu/events/PM_NEST_PAIR0_ADD - /sys/devices/cpu/events/PM_INST_FROM_L3MISS - /sys/devices/cpu/events/PM_EE_OFF_EXT_INT - /sys/devices/cpu/events/PM_INST_PTEG_FROM_DMEM - /sys/devices/cpu/events/PM_INST_FROM_DL2L3_MOD - /sys/devices/cpu/events/PM_PMC6_OVERFLOW - /sys/devices/cpu/events/PM_VSU_2FLOP_DOUBLE - /sys/devices/cpu/events/PM_TLB_MISS - /sys/devices/cpu/events/PM_FXU_BUSY - /sys/devices/cpu/events/PM_L2_RCLD_DISP_FAIL_OTHER - /sys/devices/cpu/events/PM_LSU_REJECT_LMQ_FULL - /sys/devices/cpu/events/PM_IC_RELOAD_SHR - /sys/devices/cpu/events/PM_GRP_MRK - /sys/devices/cpu/events/PM_MRK_ST_NEST - /sys/devices/cpu/events/PM_VSU1_FSQRT_FDIV - /sys/devices/cpu/events/PM_LSU0_FLUSH_LRQ - /sys/devices/cpu/events/PM_LARX_LSU0 - /sys/devices/cpu/events/PM_IBUF_FULL_CYC - /sys/devices/cpu/events/PM_MRK_DATA_FROM_DL2L3_SHR_CYC - /sys/devices/cpu/events/PM_LSU_DC_PREF_STREAM_ALLOC - /sys/devices/cpu/events/PM_GRP_MRK_CYC - /sys/devices/cpu/events/PM_MRK_DATA_FROM_RL2L3_SHR_CYC - /sys/devices/cpu/events/PM_L2_GLOB_GUESS_CORRECT - /sys/devices/cpu/events/PM_LSU_REJECT_LHS - /sys/devices/cpu/events/PM_MRK_DATA_FROM_LMEM - /sys/devices/cpu/events/PM_INST_PTEG_FROM_L3 - /sys/devices/cpu/events/PM_FREQ_DOWN - /sys/devices/cpu/events/PM_PB_RETRY_NODE_PUMP - /sys/devices/cpu/events/PM_INST_FROM_RL2L3_SHR - /sys/devices/cpu/events/PM_MRK_INST_ISSUED - /sys/devices/cpu/events/PM_PTEG_FROM_L3MISS - /sys/devices/cpu/events/PM_RUN_PURR - /sys/devices/cpu/events/PM_MRK_GRP_IC_MISS - /sys/devices/cpu/events/PM_MRK_DATA_FROM_L3 - /sys/devices/cpu/events/PM_PTEG_FROM_RL2L3_SHR - /sys/devices/cpu/events/PM_LSU_FLUSH_LRQ - /sys/devices/cpu/events/PM_MRK_DERAT_MISS_64K - /sys/devices/cpu/events/PM_INST_PTEG_FROM_DL2L3_MOD - /sys/devices/cpu/events/PM_L2_ST_MISS - /sys/devices/cpu/events/PM_MRK_PTEG_FROM_L21_SHR - /sys/devices/cpu/events/PM_LWSYNC - /sys/devices/cpu/events/PM_LSU0_DC_PREF_STREAM_CONFIRM_STRIDE - /sys/devices/cpu/events/PM_MRK_LSU_FLUSH_LRQ - /sys/devices/cpu/events/PM_INST_IMC_MATCH_CMPL - /sys/devices/cpu/events/PM_NEST_PAIR3_AND - /sys/devices/cpu/events/PM_PB_RETRY_SYS_PUMP - /sys/devices/cpu/events/PM_MRK_INST_FIN - /sys/devices/cpu/events/PM_MRK_PTEG_FROM_DL2L3_SHR - /sys/devices/cpu/events/PM_INST_FROM_L31_MOD - /sys/devices/cpu/events/PM_MRK_DTLB_MISS_64K - /sys/devices/cpu/events/PM_LSU_FIN - /sys/devices/cpu/events/PM_MRK_LSU_REJECT - /sys/devices/cpu/events/PM_L2_CO_FAIL_BUSY - /sys/devices/cpu/events/PM_MEM0_WQ_DISP - /sys/devices/cpu/events/PM_DATA_FROM_L31_MOD - /sys/devices/cpu/events/PM_THERMAL_WARN - /sys/devices/cpu/events/PM_VSU0_4FLOP - /sys/devices/cpu/events/PM_BR_MPRED_CCACHE - /sys/devices/cpu/events/PM_L1_DEMAND_WRITE - /sys/devices/cpu/events/PM_FLUSH_BR_MPRED - /sys/devices/cpu/events/PM_MRK_DTLB_MISS_16G - /sys/devices/cpu/events/PM_MRK_PTEG_FROM_DMEM - /sys/devices/cpu/events/PM_L2_RCST_DISP - /sys/devices/cpu/events/PM_LSU_PARTIAL_CDF - /sys/devices/cpu/events/PM_DISP_CLB_HELD_SB - /sys/devices/cpu/events/PM_VSU0_FMA_DOUBLE - /sys/devices/cpu/events/PM_FXU0_BUSY_FXU1_IDLE - /sys/devices/cpu/events/PM_IC_DEMAND_CYC - /sys/devices/cpu/events/PM_MRK_DATA_FROM_L21_SHR - /sys/devices/cpu/events/PM_MRK_LSU_FLUSH_UST - /sys/devices/cpu/events/PM_INST_PTEG_FROM_L3MISS - /sys/devices/cpu/events/PM_VSU_DENORM - /sys/devices/cpu/events/PM_MRK_LSU_PARTIAL_CDF - /sys/devices/cpu/events/PM_INST_FROM_L21_SHR - /sys/devices/cpu/events/PM_IC_PREF_WRITE - /sys/devices/cpu/events/PM_BR_PRED - /sys/devices/cpu/events/PM_INST_FROM_DMEM - /sys/devices/cpu/events/PM_IC_PREF_CANCEL_ALL - /sys/devices/cpu/events/PM_LSU_DC_PREF_STREAM_CONFIRM - /sys/devices/cpu/events/PM_MRK_LSU_FLUSH_SRQ - /sys/devices/cpu/events/PM_MRK_FIN_STALL_CYC - /sys/devices/cpu/events/PM_L2_RCST_DISP_FAIL_OTHER - /sys/devices/cpu/events/PM_VSU1_DD_ISSUED - /sys/devices/cpu/events/PM_PTEG_FROM_L31_SHR - /sys/devices/cpu/events/PM_DATA_FROM_L21_SHR - /sys/devices/cpu/events/PM_LSU0_NCLD - /sys/devices/cpu/events/PM_VSU1_4FLOP - /sys/devices/cpu/events/PM_VSU1_8FLOP - /sys/devices/cpu/events/PM_VSU_8FLOP - /sys/devices/cpu/events/PM_LSU_LMQ_SRQ_EMPTY_CYC - /sys/devices/cpu/events/PM_DTLB_MISS_64K - /sys/devices/cpu/events/PM_THRD_CONC_RUN_INST - /sys/devices/cpu/events/PM_MRK_PTEG_FROM_L2 - /sys/devices/cpu/events/PM_PB_SYS_PUMP - /sys/devices/cpu/events/PM_VSU_FIN - /sys/devices/cpu/events/PM_MRK_DATA_FROM_L31_MOD - /sys/devices/cpu/events/PM_THRD_PRIO_0_1_CYC - /sys/devices/cpu/events/PM_DERAT_MISS_64K - /sys/devices/cpu/events/PM_PMC2_REWIND - /sys/devices/cpu/events/PM_INST_FROM_L2 - /sys/devices/cpu/events/PM_GRP_BR_MPRED_NONSPEC - /sys/devices/cpu/events/PM_INST_DISP - /sys/devices/cpu/events/PM_MEM0_RD_CANCEL_TOTAL - /sys/devices/cpu/events/PM_LSU0_DC_PREF_STREAM_CONFIRM - /sys/devices/cpu/events/PM_L1_DCACHE_RELOAD_VALID - /sys/devices/cpu/events/PM_VSU_SCALAR_DOUBLE_ISSUED - /sys/devices/cpu/events/PM_L3_PREF_HIT - /sys/devices/cpu/events/PM_MRK_PTEG_FROM_L31_MOD - /sys/devices/cpu/events/PM_MRK_FXU_FIN - /sys/devices/cpu/events/PM_PMC4_OVERFLOW - /sys/devices/cpu/events/PM_MRK_PTEG_FROM_L3 - /sys/devices/cpu/events/PM_LSU0_LMQ_LHR_MERGE - /sys/devices/cpu/events/PM_BTAC_HIT - /sys/devices/cpu/events/PM_L3_RD_BUSY - /sys/devices/cpu/events/PM_LSU0_L1_SW_PREF - /sys/devices/cpu/events/PM_INST_FROM_L2MISS - /sys/devices/cpu/events/PM_LSU0_DC_PREF_STREAM_ALLOC - /sys/devices/cpu/events/PM_L2_ST - /sys/devices/cpu/events/PM_VSU0_DENORM - /sys/devices/cpu/events/PM_MRK_DATA_FROM_DL2L3_SHR - /sys/devices/cpu/events/PM_BR_PRED_CR_TA - /sys/devices/cpu/events/PM_VSU0_FCONV - /sys/devices/cpu/events/PM_MRK_LSU_FLUSH_ULD - /sys/devices/cpu/events/PM_BTAC_MISS - /sys/devices/cpu/events/PM_MRK_LD_MISS_EXPOSED_CYC_COUNT - /sys/devices/cpu/events/PM_MRK_DATA_FROM_L2 - /sys/devices/cpu/events/PM_LSU_DCACHE_RELOAD_VALID - /sys/devices/cpu/events/PM_VSU_FMA - /sys/devices/cpu/events/PM_LSU0_FLUSH_SRQ - /sys/devices/cpu/events/PM_LSU1_L1_PREF - /sys/devices/cpu/events/PM_IOPS_CMPL - /sys/devices/cpu/events/PM_L2_SYS_PUMP - /sys/devices/cpu/events/PM_L2_RCLD_BUSY_RC_FULL - /sys/devices/cpu/events/PM_LSU_LMQ_S0_ALLOC - /sys/devices/cpu/events/PM_FLUSH_DISP_SYNC - /sys/devices/cpu/events/PM_MRK_DATA_FROM_DL2L3_MOD_CYC - /sys/devices/cpu/events/PM_L2_IC_INV - /sys/devices/cpu/events/PM_MRK_DATA_FROM_L21_MOD_CYC - /sys/devices/cpu/events/PM_L3_PREF_LDST - /sys/devices/cpu/events/PM_LSU_SRQ_EMPTY_CYC - /sys/devices/cpu/events/PM_LSU_LMQ_S0_VALID - /sys/devices/cpu/events/PM_FLUSH_PARTIAL - /sys/devices/cpu/events/PM_VSU1_FMA_DOUBLE - /sys/devices/cpu/events/PM_1PLUS_PPC_DISP - /sys/devices/cpu/events/PM_DATA_FROM_L2MISS - /sys/devices/cpu/events/PM_SUSPENDED - /sys/devices/cpu/events/PM_VSU0_FMA - /sys/devices/cpu/events/PM_STCX_FAIL - /sys/devices/cpu/events/PM_VSU0_FSQRT_FDIV_DOUBLE - /sys/devices/cpu/events/PM_DC_PREF_DST - /sys/devices/cpu/events/PM_VSU1_SCAL_SINGLE_ISSUED - /sys/devices/cpu/events/PM_L3_HIT - /sys/devices/cpu/events/PM_L2_GLOB_GUESS_WRONG - /sys/devices/cpu/events/PM_MRK_DFU_FIN - /sys/devices/cpu/events/PM_INST_FROM_L1 - /sys/devices/cpu/events/PM_IC_DEMAND_REQ - /sys/devices/cpu/events/PM_VSU1_FSQRT_FDIV_DOUBLE - /sys/devices/cpu/events/PM_VSU1_FMA - /sys/devices/cpu/events/PM_MRK_LD_MISS_L1 - /sys/devices/cpu/events/PM_VSU0_2FLOP_DOUBLE - /sys/devices/cpu/events/PM_LSU_DC_PREF_STRIDED_STREAM_CONFIRM - /sys/devices/cpu/events/PM_INST_PTEG_FROM_L31_SHR - /sys/devices/cpu/events/PM_MRK_LSU_REJECT_ERAT_MISS - /sys/devices/cpu/events/PM_MRK_DATA_FROM_L2MISS - /sys/devices/cpu/events/PM_DATA_FROM_RL2L3_SHR - /sys/devices/cpu/events/PM_INST_FROM_PREF - /sys/devices/cpu/events/PM_VSU1_SQ - /sys/devices/cpu/events/PM_L2_LD_DISP - /sys/devices/cpu/events/PM_L2_DISP_ALL - /sys/devices/cpu/events/PM_THRD_GRP_CMPL_BOTH_CYC - /sys/devices/cpu/events/PM_VSU_FSQRT_FDIV_DOUBLE - /sys/devices/cpu/events/PM_INST_PTEG_FROM_DL2L3_SHR - /sys/devices/cpu/events/PM_VSU_1FLOP - /sys/devices/cpu/events/PM_HV_CYC - /sys/devices/cpu/events/PM_MRK_LSU_FIN - /sys/devices/cpu/events/PM_MRK_DATA_FROM_RL2L3_SHR - /sys/devices/cpu/events/PM_DTLB_MISS_16M - /sys/devices/cpu/events/PM_LSU1_LMQ_LHR_MERGE - /sys/devices/cpu/events/PM_IFU_FIN - /sys/devices/cpu/events/PM_1THRD_CON_RUN_INSTR - /sys/devices/cpu/events/PM_CMPLU_STALL_COUNT - /sys/devices/cpu/events/PM_MEM0_PB_RD_CL - /sys/devices/cpu/events/PM_THRD_1_RUN_CYC - /sys/devices/cpu/events/PM_THRD_2_CONC_RUN_INSTR - /sys/devices/cpu/events/PM_THRD_2_RUN_CYC - /sys/devices/cpu/events/PM_THRD_3_CONC_RUN_INST - /sys/devices/cpu/events/PM_THRD_3_RUN_CYC - /sys/devices/cpu/events/PM_THRD_4_CONC_RUN_INST - /sys/devices/cpu/events/PM_THRD_4_RUN_CYC +What: /sys/bus/event_source/devices//events/ +Date: 2014/02/24 +Contact: Linux kernel mailing list +Description: Per-pmu performance monitoring events specific to the running system -Date: 2013/01/08 + Each file (except for some of those with a '.' in them, '.unit' + and '.scale') in the 'events' directory describes a single + performance monitoring event supported by the . The name + of the file is the name of the event. + + File contents: + + [=][,[=]]... + + Where is one of the terms listed under + /sys/bus/event_source/devices//format/ and is + a number is base-16 format with a '0x' prefix (lowercase only). + If a is specified alone (without an assigned value), it + is implied that 0x1 is assigned to that . + Examples (each of these lines would be in a seperate file): + + event=0x2abc + event=0x423,inv,cmask=0x3 + domain=0x1,offset=0x8,starting_index=0xffff + + Each of the assignments indicates a value to be assigned to a + particular set of bits (as defined by the format file + corresponding to the ) in the perf_event structure passed + to the perf_open syscall. + +What: /sys/bus/event_source/devices//events/.unit +Date: 2014/02/24 Contact: Linux kernel mailing list - Linux Powerpc mailing list +Description: Perf event units -Description: POWER-systems specific performance monitoring events + A string specifying the English plural numerical unit that + (once multiplied by .scale) represents. - A collection of performance monitoring events that may be - supported by the POWER CPU. These events can be monitored - using the 'perf(1)' tool. + Example: - These events may not be supported by other CPUs. + Joules - The contents of each file would look like: +What: /sys/bus/event_source/devices//events/.scale +Date: 2014/02/24 +Contact: Linux kernel mailing list +Description: Perf event scaling factors - event=0xNNNN + A string representing a floating point value expressed in + scientific notation to be multiplied by the event count + recieved from the kernel to match the unit specified in the + .unit file. - where 'N' is a hex digit and the number '0xNNNN' shows the - "raw code" for the perf event identified by the file's - "basename". + Example: + + 2.3283064365386962890625e-10 - Further, multiple terms like 'event=0xNNNN' can be specified - and separated with comma. All available terms are defined in - the /sys/bus/event_source/devices//format file. + This is provided to avoid performing floating point arithmetic + in the kernel. diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power index 78c7baca35879103d6de56d08e1c7bd4724bc9aa..909e7602c717aa4555e36be1ef6d1591e420c7cd 100644 --- a/Documentation/ABI/testing/sysfs-class-power +++ b/Documentation/ABI/testing/sysfs-class-power @@ -18,3 +18,17 @@ Description: This file is writeable and can be used to set the assumed battery 'full level'. As batteries age, this value has to be amended over time. + +What: /sys/class/power_supply/max14577-charger/device/fast_charge_timer +Date: October 2014 +KernelVersion: 3.18.0 +Contact: Krzysztof Kozlowski +Description: + This entry shows and sets the maximum time the max14577 + charger operates in fast-charge mode. When the timer expires + the device will terminate fast-charge mode (charging current + will drop to 0 A) and will trigger interrupt. + + Valid values: + - 5, 6 or 7 (hours), + - 0: disabled. diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index bacefc5b222ec0dff365e83c63191859d5803a22..be35bc328b775948cd10d1c2cf3f8df76256aeb2 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -291,10 +291,9 @@ char *date; Device Registration A number of functions are provided to help with device registration. - The functions deal with PCI, USB and platform devices, respectively. + The functions deal with PCI and platform devices, respectively. !Edrivers/gpu/drm/drm_pci.c -!Edrivers/gpu/drm/drm_usb.c !Edrivers/gpu/drm/drm_platform.c New drivers that no longer rely on the services provided by the @@ -3386,6 +3385,13 @@ void (*disable_vblank) (struct drm_device *dev, int crtc); by scheduling a timer. The delay is accessible through the vblankoffdelay module parameter or the drm_vblank_offdelay global variable and expressed in milliseconds. Its default value is 5000 ms. + Zero means never disable, and a negative value means disable immediately. + Drivers may override the behaviour by setting the + drm_device + vblank_disable_immediate flag, which when set + causes vblank interrupts to be disabled immediately regardless of the + drm_vblank_offdelay value. The flag should only be set if there's a + properly working hardware vblank counter present. When a vertical blanking interrupt occurs drivers only need to call the @@ -3400,6 +3406,7 @@ void (*disable_vblank) (struct drm_device *dev, int crtc); Vertical Blanking and Interrupt Handling Functions Reference !Edrivers/gpu/drm/drm_irq.c +!Finclude/drm/drmP.h drm_crtc_vblank_waitqueue @@ -3918,6 +3925,11 @@ int num_ioctls; !Pdrivers/gpu/drm/i915/i915_cmd_parser.c batch buffer command parser !Idrivers/gpu/drm/i915/i915_cmd_parser.c + + Logical Rings, Logical Ring Contexts and Execlists +!Pdrivers/gpu/drm/i915/intel_lrc.c Logical Rings, Logical Ring Contexts and Execlists +!Idrivers/gpu/drm/i915/intel_lrc.c + diff --git a/Documentation/acpi/enumeration.txt b/Documentation/acpi/enumeration.txt index e182be5e3c83cb553341f7969678370e649fcb2d..b60d2ab69497bc66b47e752d1f7588b3cdbb93ab 100644 --- a/Documentation/acpi/enumeration.txt +++ b/Documentation/acpi/enumeration.txt @@ -312,3 +312,30 @@ a code like this: There are also devm_* versions of these functions which release the descriptors once the device is released. + +MFD devices +~~~~~~~~~~~ +The MFD devices register their children as platform devices. For the child +devices there needs to be an ACPI handle that they can use to reference +parts of the ACPI namespace that relate to them. In the Linux MFD subsystem +we provide two ways: + + o The children share the parent ACPI handle. + o The MFD cell can specify the ACPI id of the device. + +For the first case, the MFD drivers do not need to do anything. The +resulting child platform device will have its ACPI_COMPANION() set to point +to the parent device. + +If the ACPI namespace has a device that we can match using an ACPI id, +the id should be set like: + + static struct mfd_cell my_subdevice_cell = { + .name = "my_subdevice", + /* set the resources relative to the parent */ + .acpi_pnpid = "XYZ0001", + }; + +The ACPI id "XYZ0001" is then used to lookup an ACPI device directly under +the MFD device and if found, that ACPI companion device is bound to the +resulting child platform device. diff --git a/Documentation/binfmt_misc.txt b/Documentation/binfmt_misc.txt index c1ed6948ba80e7f16fd313b9bab431118e18f31e..6b1de70583715d7728a7a31b4612564b0178679b 100644 --- a/Documentation/binfmt_misc.txt +++ b/Documentation/binfmt_misc.txt @@ -15,39 +15,50 @@ First you must mount binfmt_misc: mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc To actually register a new binary type, you have to set up a string looking like -:name:type:offset:magic:mask:interpreter:flags (where you can choose the ':' upon -your needs) and echo it to /proc/sys/fs/binfmt_misc/register. +:name:type:offset:magic:mask:interpreter:flags (where you can choose the ':' +upon your needs) and echo it to /proc/sys/fs/binfmt_misc/register. + Here is what the fields mean: - 'name' is an identifier string. A new /proc file will be created with this - name below /proc/sys/fs/binfmt_misc + name below /proc/sys/fs/binfmt_misc; cannot contain slashes '/' for obvious + reasons. - 'type' is the type of recognition. Give 'M' for magic and 'E' for extension. - 'offset' is the offset of the magic/mask in the file, counted in bytes. This - defaults to 0 if you omit it (i.e. you write ':name:type::magic...') + defaults to 0 if you omit it (i.e. you write ':name:type::magic...'). Ignored + when using filename extension matching. - 'magic' is the byte sequence binfmt_misc is matching for. The magic string - may contain hex-encoded characters like \x0a or \xA4. In a shell environment - you will have to write \\x0a to prevent the shell from eating your \. + may contain hex-encoded characters like \x0a or \xA4. Note that you must + escape any NUL bytes; parsing halts at the first one. In a shell environment + you might have to write \\x0a to prevent the shell from eating your \. If you chose filename extension matching, this is the extension to be recognised (without the '.', the \x0a specials are not allowed). Extension - matching is case sensitive! + matching is case sensitive, and slashes '/' are not allowed! - 'mask' is an (optional, defaults to all 0xff) mask. You can mask out some bits from matching by supplying a string like magic and as long as magic. - The mask is anded with the byte sequence of the file. + The mask is anded with the byte sequence of the file. Note that you must + escape any NUL bytes; parsing halts at the first one. Ignored when using + filename extension matching. - 'interpreter' is the program that should be invoked with the binary as first argument (specify the full path) - 'flags' is an optional field that controls several aspects of the invocation - of the interpreter. It is a string of capital letters, each controls a certain - aspect. The following flags are supported - - 'P' - preserve-argv[0]. Legacy behavior of binfmt_misc is to overwrite the - original argv[0] with the full path to the binary. When this flag is - included, binfmt_misc will add an argument to the argument vector for - this purpose, thus preserving the original argv[0]. + of the interpreter. It is a string of capital letters, each controls a + certain aspect. The following flags are supported - + 'P' - preserve-argv[0]. Legacy behavior of binfmt_misc is to overwrite + the original argv[0] with the full path to the binary. When this + flag is included, binfmt_misc will add an argument to the argument + vector for this purpose, thus preserving the original argv[0]. + e.g. If your interp is set to /bin/foo and you run `blah` (which is + in /usr/local/bin), then the kernel will execute /bin/foo with + argv[] set to ["/bin/foo", "/usr/local/bin/blah", "blah"]. The + interp has to be aware of this so it can execute /usr/local/bin/blah + with argv[] set to ["blah"]. 'O' - open-binary. Legacy behavior of binfmt_misc is to pass the full path of the binary to the interpreter as an argument. When this flag is included, binfmt_misc will open the file for reading and pass its descriptor as an argument, instead of the full path, thus allowing - the interpreter to execute non-readable binaries. This feature should - be used with care - the interpreter has to be trusted not to emit - the contents of the non-readable binary. + the interpreter to execute non-readable binaries. This feature + should be used with care - the interpreter has to be trusted not to + emit the contents of the non-readable binary. 'C' - credentials. Currently, the behavior of binfmt_misc is to calculate the credentials and security token of the new process according to the interpreter. When this flag is included, these attributes are @@ -58,7 +69,7 @@ Here is what the fields mean: There are some restrictions: - - the whole register string may not exceed 255 characters + - the whole register string may not exceed 1920 characters - the magic must reside in the first 128 bytes of the file, i.e. offset+size(magic) has to be less than 128 - the interpreter string may not exceed 127 characters @@ -110,7 +121,4 @@ passes it the full filename (or the file descriptor) to use. Using $PATH can cause unexpected behaviour and can be a security hazard. -There is a web page about binfmt_misc at -http://www.tat.physik.uni-tuebingen.de - Richard Günther diff --git a/Documentation/block/data-integrity.txt b/Documentation/block/data-integrity.txt index 2d735b0ae383b625c87f37ed2603ec829adf6746..f56ec97f0d1492c3b5b5ea57bae974cb087b2e7d 100644 --- a/Documentation/block/data-integrity.txt +++ b/Documentation/block/data-integrity.txt @@ -129,11 +129,11 @@ interface for this is being worked on. 4.1 BIO The data integrity patches add a new field to struct bio when -CONFIG_BLK_DEV_INTEGRITY is enabled. bio->bi_integrity is a pointer -to a struct bip which contains the bio integrity payload. Essentially -a bip is a trimmed down struct bio which holds a bio_vec containing -the integrity metadata and the required housekeeping information (bvec -pool, vector count, etc.) +CONFIG_BLK_DEV_INTEGRITY is enabled. bio_integrity(bio) returns a +pointer to a struct bip which contains the bio integrity payload. +Essentially a bip is a trimmed down struct bio which holds a bio_vec +containing the integrity metadata and the required housekeeping +information (bvec pool, vector count, etc.) A kernel subsystem can enable data integrity protection on a bio by calling bio_integrity_alloc(bio). This will allocate and attach the @@ -192,16 +192,6 @@ will require extra work due to the application tag. supported by the block device. - int bdev_integrity_enabled(block_device, int rw); - - bdev_integrity_enabled() will return 1 if the block device - supports integrity metadata transfer for the data direction - specified in 'rw'. - - bdev_integrity_enabled() honors the write_generate and - read_verify flags in sysfs and will respond accordingly. - - int bio_integrity_prep(bio); To generate IMD for WRITE and to set up buffers for READ, the @@ -216,36 +206,6 @@ will require extra work due to the application tag. bio_integrity_enabled() returned 1. - int bio_integrity_tag_size(bio); - - If the filesystem wants to use the application tag space it will - first have to find out how much storage space is available. - Because tag space is generally limited (usually 2 bytes per - sector regardless of sector size), the integrity framework - supports interleaving the information between the sectors in an - I/O. - - Filesystems can call bio_integrity_tag_size(bio) to find out how - many bytes of storage are available for that particular bio. - - Another option is bdev_get_tag_size(block_device) which will - return the number of available bytes per hardware sector. - - - int bio_integrity_set_tag(bio, void *tag_buf, len); - - After a successful return from bio_integrity_prep(), - bio_integrity_set_tag() can be used to attach an opaque tag - buffer to a bio. Obviously this only makes sense if the I/O is - a WRITE. - - - int bio_integrity_get_tag(bio, void *tag_buf, len); - - Similarly, at READ I/O completion time the filesystem can - retrieve the tag buffer using bio_integrity_get_tag(). - - 5.3 PASSING EXISTING INTEGRITY METADATA Filesystems that either generate their own integrity metadata or @@ -298,8 +258,6 @@ will require extra work due to the application tag. .name = "STANDARDSBODY-TYPE-VARIANT-CSUM", .generate_fn = my_generate_fn, .verify_fn = my_verify_fn, - .get_tag_fn = my_get_tag_fn, - .set_tag_fn = my_set_tag_fn, .tuple_size = sizeof(struct my_tuple_size), .tag_size = , }; @@ -321,7 +279,5 @@ will require extra work due to the application tag. are available per hardware sector. For DIF this is either 2 or 0 depending on the value of the Control Mode Page ATO bit. - See 6.2 for a description of get_tag_fn and set_tag_fn. - ---------------------------------------------------------------------- 2007-12-24 Martin K. Petersen diff --git a/Documentation/devicetree/bindings/clock/exynos3250-clock.txt b/Documentation/devicetree/bindings/clock/exynos3250-clock.txt index aadc9c59e2d17ea31e2ccdc82a3c688de4106a66..f57d9dd9ea8530ae9c8d02723b1bc15d38002497 100644 --- a/Documentation/devicetree/bindings/clock/exynos3250-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos3250-clock.txt @@ -7,6 +7,8 @@ Required Properties: - compatible: should be one of the following. - "samsung,exynos3250-cmu" - controller compatible with Exynos3250 SoC. + - "samsung,exynos3250-cmu-dmc" - controller compatible with + Exynos3250 SoC for Dynamic Memory Controller domain. - reg: physical base address of the controller and length of memory mapped region. @@ -20,7 +22,7 @@ All available clocks are defined as preprocessor macros in dt-bindings/clock/exynos3250.h header and can be used in device tree sources. -Example 1: An example of a clock controller node is listed below. +Example 1: Examples of clock controller nodes are listed below. cmu: clock-controller@10030000 { compatible = "samsung,exynos3250-cmu"; @@ -28,6 +30,12 @@ Example 1: An example of a clock controller node is listed below. #clock-cells = <1>; }; + cmu_dmc: clock-controller@105C0000 { + compatible = "samsung,exynos3250-cmu-dmc"; + reg = <0x105C0000 0x2000>; + #clock-cells = <1>; + }; + Example 2: UART controller node that consumes the clock generated by the clock controller. Refer to the standard clock bindings for information about 'clocks' and 'clock-names' property. diff --git a/Documentation/devicetree/bindings/clock/gpio-gate-clock.txt b/Documentation/devicetree/bindings/clock/gpio-gate-clock.txt new file mode 100644 index 0000000000000000000000000000000000000000..d3379ff9b84b120c9a2668e7a0ec8827ecc97965 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/gpio-gate-clock.txt @@ -0,0 +1,21 @@ +Binding for simple gpio gated clock. + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be "gpio-gate-clock". +- #clock-cells : from common clock binding; shall be set to 0. +- enable-gpios : GPIO reference for enabling and disabling the clock. + +Optional properties: +- clocks: Maximum of one parent clock is supported. + +Example: + clock { + compatible = "gpio-gate-clock"; + clocks = <&parentclk>; + #clock-cells = <0>; + enable-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>; + }; diff --git a/Documentation/devicetree/bindings/clock/maxim,max77686.txt b/Documentation/devicetree/bindings/clock/maxim,max77686.txt index 96ce71bbd745ebb09a4e9bbe44378cdf0a728cc7..9c40739a661a835af3658af49f183ef98354bbc1 100644 --- a/Documentation/devicetree/bindings/clock/maxim,max77686.txt +++ b/Documentation/devicetree/bindings/clock/maxim,max77686.txt @@ -9,13 +9,21 @@ The MAX77686 contains three 32.768khz clock outputs that can be controlled Following properties should be presend in main device node of the MFD chip. Required properties: -- #clock-cells: simple one-cell clock specifier format is used, where the - only cell is used as an index of the clock inside the provider. Following - indices are allowed: + +- #clock-cells: from common clock binding; shall be set to 1. + +Optional properties: +- clock-output-names: From common clock binding. + +Each clock is assigned an identifier and client nodes can use this identifier +to specify the clock which they consume. Following indices are allowed: - 0: 32khz_ap clock, - 1: 32khz_cp clock, - 2: 32khz_pmic clock. +Clocks are defined as preprocessor macros in dt-bindings/clock/maxim,max77686.h +header and can be used in device tree sources. + Example: Node of the MFD chip max77686: max77686@09 { @@ -34,5 +42,5 @@ Example: Clock consumer node compatible = "bar,foo"; /* ... */ clock-names = "my-clock"; - clocks = <&max77686 2>; + clocks = <&max77686 MAX77686_CLK_PMIC>; }; diff --git a/Documentation/devicetree/bindings/clock/maxim,max77802.txt b/Documentation/devicetree/bindings/clock/maxim,max77802.txt new file mode 100644 index 0000000000000000000000000000000000000000..c6dc7835f06ccce77b6c497271ae5ab2ccb46c7b --- /dev/null +++ b/Documentation/devicetree/bindings/clock/maxim,max77802.txt @@ -0,0 +1,44 @@ +Binding for Maxim MAX77802 32k clock generator block + +This is a part of device tree bindings of MAX77802 multi-function device. +More information can be found in bindings/mfd/max77802.txt file. + +The MAX77802 contains two 32.768khz clock outputs that can be controlled +(gated/ungated) over I2C. + +Following properties should be present in main device node of the MFD chip. + +Required properties: +- #clock-cells: From common clock binding; shall be set to 1. + +Optional properties: +- clock-output-names: From common clock binding. + +Each clock is assigned an identifier and client nodes can use this identifier +to specify the clock which they consume. Following indices are allowed: + - 0: 32khz_ap clock, + - 1: 32khz_cp clock. + +Clocks are defined as preprocessor macros in dt-bindings/clock/maxim,max77802.h +header and can be used in device tree sources. + +Example: Node of the MFD chip + + max77802: max77802@09 { + compatible = "maxim,max77802"; + interrupt-parent = <&wakeup_eint>; + interrupts = <26 0>; + reg = <0x09>; + #clock-cells = <1>; + + /* ... */ + }; + +Example: Clock consumer node + + foo@0 { + compatible = "bar,foo"; + /* ... */ + clock-names = "my-clock"; + clocks = <&max77802 MAX77802_CLK_32K_AP>; + }; diff --git a/Documentation/devicetree/bindings/clock/pxa-clock.txt b/Documentation/devicetree/bindings/clock/pxa-clock.txt new file mode 100644 index 0000000000000000000000000000000000000000..4b4a9024bd990bfa4b714b64f71a785da5494ea9 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/pxa-clock.txt @@ -0,0 +1,16 @@ +* Clock bindings for Marvell PXA chips + +Required properties: +- compatible: Should be "marvell,pxa-clocks" +- #clock-cells: Should be <1> + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell (see include/.../pxa-clock.h). + +Examples: + +pxa2xx_clks: pxa2xx_clks@41300004 { + compatible = "marvell,pxa-clocks"; + #clock-cells = <1>; + status = "okay"; +}; diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt index 8f1424f0fa439cdfac3e16e0e6e90ef039218ab0..a5f52238c80d5f9b9560914f59e653e772b42bea 100644 --- a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt @@ -15,6 +15,7 @@ Required Properties: - "renesas,r8a7779-mstp-clocks" for R8A7779 (R-Car H1) MSTP gate clocks - "renesas,r8a7790-mstp-clocks" for R8A7790 (R-Car H2) MSTP gate clocks - "renesas,r8a7791-mstp-clocks" for R8A7791 (R-Car M2) MSTP gate clocks + - "renesas,r8a7794-mstp-clocks" for R8A7794 (R-Car E2) MSTP gate clocks - "renesas,sh73a0-mstp-clocks" for SH73A0 (SH-MobileAG5) MSTP gate clocks - "renesas,cpg-mstp-clock" for generic MSTP gate clocks - reg: Base address and length of the I/O mapped registers used by the MSTP diff --git a/Documentation/devicetree/bindings/clock/renesas,rcar-gen2-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,rcar-gen2-cpg-clocks.txt index 7b41c2fe54dbcfbd950b9b650d6bfc2ae374a0c0..e6ad35b894f919f537dbf58cbdea6fffd9cddfc9 100644 --- a/Documentation/devicetree/bindings/clock/renesas,rcar-gen2-cpg-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,rcar-gen2-cpg-clocks.txt @@ -8,6 +8,7 @@ Required Properties: - compatible: Must be one of - "renesas,r8a7790-cpg-clocks" for the r8a7790 CPG - "renesas,r8a7791-cpg-clocks" for the r8a7791 CPG + - "renesas,r8a7794-cpg-clocks" for the r8a7794 CPG - "renesas,rcar-gen2-cpg-clocks" for the generic R-Car Gen2 CPG - reg: Base address and length of the memory resource used by the CPG diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index d3a5c3c6d6774602667a69e9e93e7511039ef3f4..ed116df9c3e769a2ceef9cb4d1dd1eaf44473f38 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -46,7 +46,11 @@ Required properties: "allwinner,sun6i-a31-apb2-div-clk" - for the APB2 gates on A31 "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 "allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23 + "allwinner,sun5i-a13-mbus-clk" - for the MBUS clock on A13 + "allwinner,sun4i-a10-mmc-output-clk" - for the MMC output clock on A10 + "allwinner,sun4i-a10-mmc-sample-clk" - for the MMC sample clock on A10 "allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks + "allwinner,sun8i-a23-mbus-clk" - for the MBUS clock on A23 "allwinner,sun7i-a20-out-clk" - for the external output clocks "allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31 "allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20 diff --git a/Documentation/devicetree/bindings/dma/qcom_adm.txt b/Documentation/devicetree/bindings/dma/qcom_adm.txt new file mode 100644 index 0000000000000000000000000000000000000000..9bcab911598200b159ef0c032a95d20990cffd53 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/qcom_adm.txt @@ -0,0 +1,62 @@ +QCOM ADM DMA Controller + +Required properties: +- compatible: must contain "qcom,adm" for IPQ/APQ8064 and MSM8960 +- reg: Address range for DMA registers +- interrupts: Should contain one interrupt shared by all channels +- #dma-cells: must be <2>. First cell denotes the channel number. Second cell + denotes CRCI (client rate control interface) flow control assignment. +- clocks: Should contain the core clock and interface clock. +- clock-names: Must contain "core" for the core clock and "iface" for the + interface clock. +- resets: Must contain an entry for each entry in reset names. +- reset-names: Must include the following entries: + - clk + - c0 + - c1 + - c2 +- qcom,ee: indicates the security domain identifier used in the secure world. + +Example: + adm_dma: dma@18300000 { + compatible = "qcom,adm"; + reg = <0x18300000 0x100000>; + interrupts = <0 170 0>; + #dma-cells = <2>; + + clocks = <&gcc ADM0_CLK>, <&gcc ADM0_PBUS_CLK>; + clock-names = "core", "iface"; + + resets = <&gcc ADM0_RESET>, + <&gcc ADM0_C0_RESET>, + <&gcc ADM0_C1_RESET>, + <&gcc ADM0_C2_RESET>; + reset-names = "clk", "c0", "c1", "c2"; + qcom,ee = <0>; + }; + +DMA clients must use the format descripted in the dma.txt file, using a three +cell specifier for each channel. + +Each dmas request consists of 3 cells: + 1. phandle pointing to the DMA controller + 2. channel number + 3. CRCI assignment, if applicable. If no CRCI flow control is required, use 0. + The CRCI is used for flow control. It identifies the peripheral device that + is the source/destination for the transferred data. + +Example: + + spi4: spi@1a280000 { + status = "ok"; + spi-max-frequency = <50000000>; + + pinctrl-0 = <&spi_pins>; + pinctrl-names = "default"; + + cs-gpios = <&qcom_pinmux 20 0>; + + dmas = <&adm_dma 6 9>, + <&adm_dma 5 10>; + dma-names = "rx", "tx"; + }; diff --git a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt new file mode 100644 index 0000000000000000000000000000000000000000..2291c4098730f6e81332b38fe8d045f4f3293347 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt @@ -0,0 +1,65 @@ +Xilinx AXI DMA engine, it does transfers between memory and AXI4 stream +target devices. It can be configured to have one channel or two channels. +If configured as two channels, one is to transmit to the device and another +is to receive from the device. + +Required properties: +- compatible: Should be "xlnx,axi-dma-1.00.a" +- #dma-cells: Should be <1>, see "dmas" property below +- reg: Should contain DMA registers location and length. +- dma-channel child node: Should have atleast one channel and can have upto + two channels per device. This node specifies the properties of each + DMA channel (see child node properties below). + +Optional properties: +- xlnx,include-sg: Tells whether configured for Scatter-mode in + the hardware. + +Required child node properties: +- compatible: It should be either "xlnx,axi-dma-mm2s-channel" or + "xlnx,axi-dma-s2mm-channel". +- interrupts: Should contain per channel DMA interrupts. +- xlnx,datawidth: Should contain the stream data width, take values + {32,64...1024}. + +Option child node properties: +- xlnx,include-dre: Tells whether hardware is configured for Data + Realignment Engine. + +Example: +++++++++ + +axi_dma_0: axidma@40400000 { + compatible = "xlnx,axi-dma-1.00.a"; + #dma_cells = <1>; + reg = < 0x40400000 0x10000 >; + dma-channel@40400000 { + compatible = "xlnx,axi-dma-mm2s-channel"; + interrupts = < 0 59 4 >; + xlnx,datawidth = <0x40>; + } ; + dma-channel@40400030 { + compatible = "xlnx,axi-dma-s2mm-channel"; + interrupts = < 0 58 4 >; + xlnx,datawidth = <0x40>; + } ; +} ; + + +* DMA client + +Required properties: +- dmas: a list of <[DMA device phandle] [Channel ID]> pairs, + where Channel ID is '0' for write/tx and '1' for read/rx + channel. +- dma-names: a list of DMA channel names, one per "dmas" entry + +Example: +++++++++ + +dmatest_0: dmatest@0 { + compatible ="xlnx,axi-dma-test-1.00.a"; + dmas = <&axi_dma_0 0 + &axi_dma_0 1>; + dma-names = "dma0", "dma1"; +} ; diff --git a/Documentation/devicetree/bindings/drm/tilcdc/panel.txt b/Documentation/devicetree/bindings/drm/tilcdc/panel.txt index 9301c330d1a64513116e43c6e010a7de17b74ce7..4ab9e230090793b4fc11519ed00494f9fad93e1b 100644 --- a/Documentation/devicetree/bindings/drm/tilcdc/panel.txt +++ b/Documentation/devicetree/bindings/drm/tilcdc/panel.txt @@ -18,6 +18,10 @@ Required properties: Documentation/devicetree/bindings/video/display-timing.txt for display timing binding details. +Optional properties: +- backlight: phandle of the backlight device attached to the panel +- enable-gpios: GPIO pin to enable or disable the panel + Recommended properties: - pinctrl-names, pinctrl-0: the pincontrol settings to configure muxing properly for pins that connect to TFP410 device @@ -29,6 +33,9 @@ Example: compatible = "ti,tilcdc,panel"; pinctrl-names = "default"; pinctrl-0 = <&bone_lcd3_cape_lcd_pins>; + backlight = <&backlight>; + enable-gpios = <&gpio3 19 0>; + panel-info { ac-bias = <255>; ac-bias-intrpt = <0>; diff --git a/Documentation/devicetree/bindings/gpio/gpio-restart.txt b/Documentation/devicetree/bindings/gpio/gpio-restart.txt new file mode 100644 index 0000000000000000000000000000000000000000..af3701bc15c48b131e4c77a8cf2c35bcf7ad7975 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-restart.txt @@ -0,0 +1,54 @@ +Drive a GPIO line that can be used to restart the system from a restart +handler. + +This binding supports level and edge triggered reset. At driver load +time, the driver will request the given gpio line and install a restart +handler. If the optional properties 'open-source' is not found, the GPIO line +will be driven in the inactive state. Otherwise its not driven until +the restart is initiated. + +When the system is restarted, the restart handler will be invoked in +priority order. The gpio is configured as an output, and driven active, +triggering a level triggered reset condition. This will also cause an +inactive->active edge condition, triggering positive edge triggered +reset. After a delay specified by active-delay, the GPIO is set to +inactive, thus causing an active->inactive edge, triggering negative edge +triggered reset. After a delay specified by inactive-delay, the GPIO +is driven active again. After a delay specified by wait-delay, the +restart handler completes allowing other restart handlers to be attempted. + +Required properties: +- compatible : should be "gpio-restart". +- gpios : The GPIO to set high/low, see "gpios property" in + Documentation/devicetree/bindings/gpio/gpio.txt. If the pin should be + low to reset the board set it to "Active Low", otherwise set + gpio to "Active High". + +Optional properties: +- open-source : Treat the GPIO as being open source and defer driving + it to when the restart is initiated. If this optional property is not + specified, the GPIO is initialized as an output in its inactive state. +- priority : A priority ranging from 0 to 255 (default 128) according to + the following guidelines: + 0: Restart handler of last resort, with limited restart + capabilities + 128: Default restart handler; use if no other restart handler is + expected to be available, and/or if restart functionality is + sufficient to restart the entire system + 255: Highest priority restart handler, will preempt all other + restart handlers +- active-delay: Delay (default 100) to wait after driving gpio active [ms] +- inactive-delay: Delay (default 100) to wait after driving gpio inactive [ms] +- wait-delay: Delay (default 3000) to wait after completing restart + sequence [ms] + +Examples: + +gpio-restart { + compatible = "gpio-restart"; + gpios = <&gpio 4 0>; + priority = <128>; + active-delay = <100>; + inactive-delay = <100>; + wait-delay = <3000>; +}; diff --git a/Documentation/devicetree/bindings/i2c/i2c-axxia.txt b/Documentation/devicetree/bindings/i2c/i2c-axxia.txt new file mode 100644 index 0000000000000000000000000000000000000000..2296d782b4c23394d9d0e76cf55ad27f9d75f635 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-axxia.txt @@ -0,0 +1,30 @@ +LSI Axxia I2C + +Required properties : +- compatible : Must be "lsi,api2c" +- reg : Offset and length of the register set for the device +- interrupts : the interrupt specifier +- #address-cells : Must be <1>; +- #size-cells : Must be <0>; +- clock-names : Must contain "i2c". +- clocks: Must contain an entry for each name in clock-names. See the common + clock bindings. + +Optional properties : +- clock-frequency : Desired I2C bus clock frequency in Hz. If not specified, + the default 100 kHz frequency will be used. As only Normal and Fast modes + are supported, possible values are 100000 and 400000. + +Example : + +i2c@02010084000 { + compatible = "lsi,api2c"; + device_type = "i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x20 0x10084000 0x00 0x1000>; + interrupts = <0 19 4>; + clocks = <&clk_per>; + clock-names = "i2c"; + clock-frequency = <400000>; +}; diff --git a/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt b/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt index d4745e31f5c62c5586304cb4ada3ede996754a76..2dbc0b62daa69a80c3d04cff149717aba3ed6663 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-exynos5.txt @@ -12,6 +12,8 @@ Required properties: on Exynos5250 and Exynos5420 SoCs. -> "samsung,exynos5260-hsi2c", for i2c compatible with HSI2C available on Exynos5260 SoCs. + -> "samsung,exynos7-hsi2c", for i2c compatible with HSI2C available + on Exynos7 SoCs. - reg: physical base address of the controller and length of memory mapped region. diff --git a/Documentation/devicetree/bindings/i2c/i2c-hix5hd2.txt b/Documentation/devicetree/bindings/i2c/i2c-hix5hd2.txt new file mode 100644 index 0000000000000000000000000000000000000000..f98b37401e6eb3946468013e1bef55a629f642e6 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-hix5hd2.txt @@ -0,0 +1,24 @@ +I2C for Hisilicon hix5hd2 chipset platform + +Required properties: +- compatible: Must be "hisilicon,hix5hd2-i2c" +- reg: physical base address of the controller and length of memory mapped + region. +- interrupts: interrupt number to the cpu. +- #address-cells = <1>; +- #size-cells = <0>; +- clocks: phandles to input clocks. + +Optional properties: +- clock-frequency: Desired I2C bus frequency in Hz, otherwise defaults to 100000 +- Child nodes conforming to i2c bus binding + +Examples: +I2C0@f8b10000 { + compatible = "hisilicon,hix5hd2-i2c"; + reg = <0xf8b10000 0x1000>; + interrupts = <0 38 4>; + clocks = <&clock HIX5HD2_I2C0_RST>; + #address-cells = <1>; + #size-cells = <0>; +} diff --git a/Documentation/devicetree/bindings/i2c/ti,bq32k.txt b/Documentation/devicetree/bindings/i2c/ti,bq32k.txt new file mode 100644 index 0000000000000000000000000000000000000000..e204906b9ad3b16111825aab5082992cc4997339 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/ti,bq32k.txt @@ -0,0 +1,18 @@ +* TI BQ32000 I2C Serial Real-Time Clock + +Required properties: +- compatible: Should contain "ti,bq32000". +- reg: I2C address for chip + +Optional properties: +- trickle-resistor-ohms : Selected resistor for trickle charger + Values usable are 1120 and 20180 + Should be given if trickle charger should be enabled +- trickle-diode-disable : Do not use internal trickle charger diode + Should be given if internal trickle charger diode should be disabled +Example: + bq32000: rtc@68 { + compatible = "ti,bq32000"; + trickle-resistor-ohms = <1120>; + reg = <0x68>; + }; diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt index 5af3d9df6ecb05c5da5dbeff969bcfba18a09a9a..fbde415078e6845cc5ff862a758959d64810c316 100644 --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt @@ -35,7 +35,6 @@ catalyst,24c32 i2c serial eeprom cirrus,cs42l51 Cirrus Logic CS42L51 audio codec dallas,ds1307 64 x 8, Serial, I2C Real-Time Clock dallas,ds1338 I2C RTC with 56-Byte NV RAM -dallas,ds1339 I2C Serial Real-Time Clock dallas,ds1340 I2C RTC with Trickle Charger dallas,ds1374 I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and Reset Input/Output dallas,ds1631 High-Precision Digital Thermometer diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt index 2d0f7cd867eaede3cad0a0d163e131b46716be2b..06760503a819f5fbc15e747626ce3f734b2888ff 100644 --- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt +++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt @@ -14,6 +14,7 @@ conditions. "arm,smmu-v1" "arm,smmu-v2" "arm,mmu-400" + "arm,mmu-401" "arm,mmu-500" depending on the particular implementation and/or the diff --git a/Documentation/devicetree/bindings/mfd/arizona.txt b/Documentation/devicetree/bindings/mfd/arizona.txt index 5c7e7230984a5c064da0ef8613ded1caf78f2bb9..7bd1273f571a40fa46b7c5c0540a4fe7c9370c4a 100644 --- a/Documentation/devicetree/bindings/mfd/arizona.txt +++ b/Documentation/devicetree/bindings/mfd/arizona.txt @@ -42,6 +42,13 @@ Optional properties: the chip default will be used. If present exactly five values must be specified. + - wlf,inmode : A list of INn_MODE register values, where n is the number + of input signals. Valid values are 0 (Differential), 1 (Single-ended) and + 2 (Digital Microphone). If absent, INn_MODE registers set to 0 by default. + If present, values must be specified less than or equal to the number of + input singals. If values less than the number of input signals, elements + that has not been specifed are set to 0 by default. + - DCVDD-supply, MICVDD-supply : Power supplies, only need to be specified if they are being externally supplied. As covered in Documentation/devicetree/bindings/regulator/regulator.txt diff --git a/Documentation/devicetree/bindings/mfd/atmel-gpbr.txt b/Documentation/devicetree/bindings/mfd/atmel-gpbr.txt new file mode 100644 index 0000000000000000000000000000000000000000..a28569540683e13958d68a1c36c6f1d2ec28994e --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/atmel-gpbr.txt @@ -0,0 +1,15 @@ +* Device tree bindings for Atmel GPBR (General Purpose Backup Registers) + +The GPBR are a set of battery-backed registers. + +Required properties: +- compatible: "atmel,at91sam9260-gpbr", "syscon" +- reg: contains offset/length value of the GPBR memory + region. + +Example: + +gpbr: gpbr@fffffd50 { + compatible = "atmel,at91sam9260-gpbr", "syscon"; + reg = <0xfffffd50 0x10>; +}; diff --git a/Documentation/devicetree/bindings/mfd/hi6421.txt b/Documentation/devicetree/bindings/mfd/hi6421.txt new file mode 100644 index 0000000000000000000000000000000000000000..0d5a4466a494f6d473d9d7d85d077acf1e257e1d --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/hi6421.txt @@ -0,0 +1,38 @@ +* HI6421 Multi-Functional Device (MFD), by HiSilicon Ltd. + +Required parent device properties: +- compatible : contains "hisilicon,hi6421-pmic"; +- reg : register range space of hi6421; + +Supported Hi6421 sub-devices include: + +Device IRQ Names Supply Names Description +------ --------- ------------ ----------- +regulators : None : None : Regulators + +Required child device properties: +None. + +Example: + hi6421 { + compatible = "hisilicon,hi6421-pmic"; + reg = <0xfcc00000 0x0180>; /* 0x60 << 2 */ + + regulators { + // supply for MLC NAND/ eMMC + hi6421_vout0_reg: hi6421_vout0 { + regulator-name = "VOUT0"; + regulator-min-microvolt = <2850000>; + regulator-max-microvolt = <2850000>; + }; + + // supply for 26M Oscillator + hi6421_vout1_reg: hi6421_vout1 { + regulator-name = "VOUT1"; + regulator-min-microvolt = <1700000>; + regulator-max-microvolt = <2000000>; + regulator-boot-on; + regulator-always-on; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/max14577.txt b/Documentation/devicetree/bindings/mfd/max14577.txt new file mode 100644 index 0000000000000000000000000000000000000000..236264c10b92dc99feb4e9141ec631f9aecb4aea --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/max14577.txt @@ -0,0 +1,146 @@ +Maxim MAX14577/77836 Multi-Function Device + +MAX14577 is a Multi-Function Device with Micro-USB Interface Circuit, Li+ +Battery Charger and SFOUT LDO output for powering USB devices. It is +interfaced to host controller using I2C. + +MAX77836 additionally contains PMIC (with two LDO regulators) and Fuel Gauge. + + +Required properties: +- compatible : Must be "maxim,max14577" or "maxim,max77836". +- reg : I2C slave address for the max14577 chip (0x25 for max14577/max77836) +- interrupts : IRQ line for the chip. +- interrupt-parent : The parent interrupt controller. + + +Required nodes: + - charger : + Node for configuring the charger driver. + Required properties: + - compatible : "maxim,max14577-charger" + or "maxim,max77836-charger" + - maxim,fast-charge-uamp : Current in uA for Fast Charge; + Valid values: + - for max14577: 90000 - 950000; + - for max77836: 45000 - 475000; + - maxim,eoc-uamp : Current in uA for End-Of-Charge mode; + Valid values: + - for max14577: 50000 - 200000; + - for max77836: 5000 - 100000; + - maxim,ovp-uvolt : OverVoltage Protection Threshold in uV; + In an overvoltage condition, INT asserts and charging + stops. Valid values: + - 6000000, 6500000, 7000000, 7500000; + - maxim,constant-uvolt : Battery Constant Voltage in uV; + Valid values: + - 4000000 - 4280000 (step by 20000); + - 4350000; + + +Optional nodes: +- max14577-muic/max77836-muic : + Node used only by extcon consumers. + Required properties: + - compatible : "maxim,max14577-muic" or "maxim,max77836-muic" + +- regulators : + Required properties: + - compatible : "maxim,max14577-regulator" + or "maxim,max77836-regulator" + + May contain a sub-node per regulator from the list below. Each + sub-node should contain the constraints and initialization information + for that regulator. See regulator.txt for a description of standard + properties for these sub-nodes. + + List of valid regulator names: + - for max14577: CHARGER, SAFEOUT. + - for max77836: CHARGER, SAFEOUT, LDO1, LDO2. + + The SAFEOUT is a fixed voltage regulator so there is no need to specify + voltages for it. + + +Example: + +#include + +max14577@25 { + compatible = "maxim,max14577"; + reg = <0x25>; + interrupt-parent = <&gpx1>; + interrupts = <5 IRQ_TYPE_NONE>; + + muic: max14577-muic { + compatible = "maxim,max14577-muic"; + }; + + regulators { + compatible = "maxim,max14577-regulator"; + + SAFEOUT { + regulator-name = "SAFEOUT"; + }; + CHARGER { + regulator-name = "CHARGER"; + regulator-min-microamp = <90000>; + regulator-max-microamp = <950000>; + regulator-boot-on; + }; + }; + + charger { + compatible = "maxim,max14577-charger"; + + maxim,constant-uvolt = <4350000>; + maxim,fast-charge-uamp = <450000>; + maxim,eoc-uamp = <50000>; + maxim,ovp-uvolt = <6500000>; + }; +}; + + +max77836@25 { + compatible = "maxim,max77836"; + reg = <0x25>; + interrupt-parent = <&gpx1>; + interrupts = <5 IRQ_TYPE_NONE>; + + muic: max77836-muic { + compatible = "maxim,max77836-muic"; + }; + + regulators { + compatible = "maxim,max77836-regulator"; + + SAFEOUT { + regulator-name = "SAFEOUT"; + }; + CHARGER { + regulator-name = "CHARGER"; + regulator-min-microamp = <90000>; + regulator-max-microamp = <950000>; + regulator-boot-on; + }; + LDO1 { + regulator-name = "LDO1"; + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <2700000>; + }; + LDO2 { + regulator-name = "LDO2"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3950000>; + }; + }; + + charger { + compatible = "maxim,max77836-charger"; + + maxim,constant-uvolt = <4350000>; + maxim,fast-charge-uamp = <225000>; + maxim,eoc-uamp = <7500>; + maxim,ovp-uvolt = <6500000>; + }; +}; diff --git a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.txt b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.txt new file mode 100644 index 0000000000000000000000000000000000000000..7182b8857f57f39889a3afaec5b2366bc6f40050 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.txt @@ -0,0 +1,64 @@ + Qualcomm SPMI PMICs multi-function device bindings + +The Qualcomm SPMI series presently includes PM8941, PM8841 and PMA8084 +PMICs. These PMICs use a QPNP scheme through SPMI interface. +QPNP is effectively a partitioning scheme for dividing the SPMI extended +register space up into logical pieces, and set of fixed register +locations/definitions within these regions, with some of these regions +specifically used for interrupt handling. + +The QPNP PMICs are used with the Qualcomm Snapdragon series SoCs, and are +interfaced to the chip via the SPMI (System Power Management Interface) bus. +Support for multiple independent functions are implemented by splitting the +16-bit SPMI slave address space into 256 smaller fixed-size regions, 256 bytes +each. A function can consume one or more of these fixed-size register regions. + +Required properties: +- compatible: Should contain one of: + "qcom,pm8941" + "qcom,pm8841" + "qcom,pma8084" + or generalized "qcom,spmi-pmic". +- reg: Specifies the SPMI USID slave address for this device. + For more information see: + Documentation/devicetree/bindings/spmi/spmi.txt + +Required properties for peripheral child nodes: +- compatible: Should contain "qcom,xxx", where "xxx" is a peripheral name. + +Optional properties for peripheral child nodes: +- interrupts: Interrupts are specified as a 4-tuple. For more information + see: + Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt +- interrupt-names: Corresponding interrupt name to the interrupts property + +Each child node of SPMI slave id represents a function of the PMIC. In the +example below the rtc device node represents a peripheral of pm8941 +SID = 0. The regulator device node represents a peripheral of pm8941 SID = 1. + +Example: + + spmi { + compatible = "qcom,spmi-pmic-arb"; + + pm8941@0 { + compatible = "qcom,pm8941", "qcom,spmi-pmic"; + reg = <0x0 SPMI_USID>; + + rtc { + compatible = "qcom,rtc"; + interrupts = <0x0 0x61 0x1 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "alarm"; + }; + }; + + pm8941@1 { + compatible = "qcom,pm8941", "qcom,spmi-pmic"; + reg = <0x1 SPMI_USID>; + + regulator { + compatible = "qcom,regulator"; + regulator-name = "8941_boost"; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt b/Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt similarity index 98% rename from Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt rename to Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt index 03518dc8b6bdc71257458e053e1acd275870945f..f24f334091648bbecbe3fd314ea08aaf2f0252cc 100644 --- a/Documentation/devicetree/bindings/mfd/qcom,pm8xxx.txt +++ b/Documentation/devicetree/bindings/mfd/qcom-pm8xxx.txt @@ -61,6 +61,7 @@ The below bindings specify the set of valid subnodes. Definition: must be one of: "qcom,pm8058-rtc" "qcom,pm8921-rtc" + "qcom,pm8941-rtc" - reg: Usage: required diff --git a/Documentation/devicetree/bindings/mfd/rk808.txt b/Documentation/devicetree/bindings/mfd/rk808.txt new file mode 100644 index 0000000000000000000000000000000000000000..9e6e2592e5c88d0eace16144f9521a7b3e60dc9d --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/rk808.txt @@ -0,0 +1,177 @@ +RK808 Power Management Integrated Circuit + +Required properties: +- compatible: "rockchip,rk808" +- reg: I2C slave address +- interrupt-parent: The parent interrupt controller. +- interrupts: the interrupt outputs of the controller. +- #clock-cells: from common clock binding; shall be set to 1 (multiple clock + outputs). See for clock IDs. + +Optional properties: +- clock-output-names: From common clock binding to override the + default output clock name +- rockchip,system-power-controller: Telling whether or not this pmic is controlling + the system power. +- vcc1-supply: The input supply for DCDC_REG1 +- vcc2-supply: The input supply for DCDC_REG2 +- vcc3-supply: The input supply for DCDC_REG3 +- vcc4-supply: The input supply for DCDC_REG4 +- vcc6-supply: The input supply for LDO_REG1 and LDO_REG2 +- vcc7-supply: The input supply for LDO_REG3 and LDO_REG7 +- vcc8-supply: The input supply for SWITCH_REG1 +- vcc9-supply: The input supply for LDO_REG4 and LDO_REG5 +- vcc10-supply: The input supply for LDO_REG6 +- vcc11-supply: The input supply for LDO_REG8 +- vcc12-supply: The input supply for SWITCH_REG2 + +Regulators: All the regulators of RK808 to be instantiated shall be +listed in a child node named 'regulators'. Each regulator is represented +by a child node of the 'regulators' node. + + regulator-name { + /* standard regulator bindings here */ + }; + +Following regulators of the RK808 PMIC block are supported. Note that +the 'n' in regulator name, as in DCDC_REGn or LDOn, represents the DCDC or LDO +number as described in RK808 datasheet. + + - DCDC_REGn + - valid values for n are 1 to 4. + - LDO_REGn + - valid values for n are 1 to 8. + - SWITCH_REGn + - valid values for n are 1 to 2 + +Standard regulator bindings are used inside regulator subnodes. Check + Documentation/devicetree/bindings/regulator/regulator.txt +for more details + +Example: + rk808: pmic@1b { + compatible = "rockchip,rk808"; + clock-output-names = "xin32k", "rk808-clkout2"; + interrupt-parent = <&gpio0>; + interrupts = <4 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_int>; + reg = <0x1b>; + rockchip,system-power-controller; + wakeup-source; + #clock-cells = <1>; + + vcc8-supply = <&vcc_18>; + vcc9-supply = <&vcc_io>; + vcc10-supply = <&vcc_io>; + vcc12-supply = <&vcc_io>; + vddio-supply = <&vccio_pmu>; + + regulators { + vdd_cpu: DCDC_REG1 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <1300000>; + regulator-name = "vdd_arm"; + }; + + vdd_gpu: DCDC_REG2 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1250000>; + regulator-name = "vdd_gpu"; + }; + + vcc_ddr: DCDC_REG3 { + regulator-always-on; + regulator-boot-on; + regulator-name = "vcc_ddr"; + }; + + vcc_io: DCDC_REG4 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc_io"; + }; + + vccio_pmu: LDO_REG1 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vccio_pmu"; + }; + + vcc_tp: LDO_REG2 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc_tp"; + }; + + vdd_10: LDO_REG3 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-name = "vdd_10"; + }; + + vcc18_lcd: LDO_REG4 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc18_lcd"; + }; + + vccio_sd: LDO_REG5 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vccio_sd"; + }; + + vdd10_lcd: LDO_REG6 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-name = "vdd10_lcd"; + }; + + vcc_18: LDO_REG7 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc_18"; + }; + + vcca_codec: LDO_REG8 { + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcca_codec"; + }; + + vcc_wl: SWITCH_REG1 { + regulator-always-on; + regulator-boot-on; + regulator-name = "vcc_wl"; + }; + + vcc_lcd: SWITCH_REG2 { + regulator-always-on; + regulator-boot-on; + regulator-name = "vcc_lcd"; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/rn5t618.txt b/Documentation/devicetree/bindings/mfd/rn5t618.txt new file mode 100644 index 0000000000000000000000000000000000000000..937785a3eddc1020828568cdbfeb2d0936d10f32 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/rn5t618.txt @@ -0,0 +1,36 @@ +* Ricoh RN5T618 PMIC + +Ricoh RN5T618 is a power management IC which integrates 3 step-down +DCDC converters, 7 low-dropout regulators, a Li-ion battery charger, +fuel gauge, ADC, GPIOs and a watchdog timer. It can be controlled +through a I2C interface. + +Required properties: + - compatible: should be "ricoh,rn5t618" + - reg: the I2C slave address of the device + +Sub-nodes: + - regulators: the node is required if the regulator functionality is + needed. The valid regulator names are: DCDC1, DCDC2, DCDC3, LDO1, + LDO2, LDO3, LDO4, LDO5, LDORTC1 and LDORTC2. + The common bindings for each individual regulator can be found in: + Documentation/devicetree/bindings/regulator/regulator.txt + +Example: + + pmic@32 { + compatible = "ricoh,rn5t618"; + reg = <0x32>; + + regulators { + DCDC1 { + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1050000>; + }; + + DCDC2 { + regulator-min-microvolt = <1175000>; + regulator-max-microvolt = <1175000>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/stmpe.txt b/Documentation/devicetree/bindings/mfd/stmpe.txt index 56edb55206854aeba75d63ce2c85ab707208d12a..3fb68bfefc8b30eb149d8ddf36d7b0eadccb1ded 100644 --- a/Documentation/devicetree/bindings/mfd/stmpe.txt +++ b/Documentation/devicetree/bindings/mfd/stmpe.txt @@ -13,6 +13,7 @@ Optional properties: - interrupt-parent : Specifies which IRQ controller we're connected to - wakeup-source : Marks the input device as wakable - st,autosleep-timeout : Valid entries (ms); 4, 16, 32, 64, 128, 256, 512 and 1024 + - irq-gpio : If present, which GPIO to use for event IRQ Example: diff --git a/Documentation/devicetree/bindings/mfd/twl4030-power.txt b/Documentation/devicetree/bindings/mfd/twl4030-power.txt index b9ee7b98d3e234195ede810f6c32e6afa5a22411..3d19963312ce0a8c9e0a06f597f899df3ad08905 100644 --- a/Documentation/devicetree/bindings/mfd/twl4030-power.txt +++ b/Documentation/devicetree/bindings/mfd/twl4030-power.txt @@ -23,8 +23,13 @@ down during off-idle. Note that this does not work on all boards depending on how the external oscillator is wired. Optional properties: -- ti,use_poweroff: With this flag, the chip will initiates an ACTIVE-to-OFF or - SLEEP-to-OFF transition when the system poweroffs. + +- ti,system-power-controller: This indicates that TWL4030 is the + power supply master of the system. With this flag, the chip will + initiate an ACTIVE-to-OFF or SLEEP-to-OFF transition when the + system poweroffs. + +- ti,use_poweroff: Deprecated name for ti,system-power-controller Example: &i2c1 { diff --git a/Documentation/devicetree/bindings/mtd/atmel-nand.txt b/Documentation/devicetree/bindings/mtd/atmel-nand.txt index c4728839d0c1333098b38fe4e7aa96e3e0ab7f9e..6edc3b616e98c6a9f7547be9c5393fbda56f1206 100644 --- a/Documentation/devicetree/bindings/mtd/atmel-nand.txt +++ b/Documentation/devicetree/bindings/mtd/atmel-nand.txt @@ -36,6 +36,7 @@ Optional properties: - reg : should specify the address and size used for NFC command registers, NFC registers and NFC Sram. NFC Sram address and size can be absent if don't want to use it. + - clocks: phandle to the peripheral clock - Optional properties: - atmel,write-by-sram: boolean to enable NFC write by sram. @@ -98,6 +99,7 @@ nand0: nand@40000000 { compatible = "atmel,sama5d3-nfc"; #address-cells = <1>; #size-cells = <1>; + clocks = <&hsmc_clk> reg = < 0x70000000 0x10000000 /* NFC Command Registers */ 0xffffc000 0x00000070 /* NFC HSMC regs */ diff --git a/Documentation/devicetree/bindings/mtd/mtd-physmap.txt b/Documentation/devicetree/bindings/mtd/mtd-physmap.txt index 61c5ec850f2fb18dc150f15e4fbab0c02edc0279..6b9f680cb579bd7fbca4f1ee17f610e6c0b079bf 100644 --- a/Documentation/devicetree/bindings/mtd/mtd-physmap.txt +++ b/Documentation/devicetree/bindings/mtd/mtd-physmap.txt @@ -4,8 +4,8 @@ Flash chips (Memory Technology Devices) are often used for solid state file systems on embedded devices. - compatible : should contain the specific model of mtd chip(s) - used, if known, followed by either "cfi-flash", "jedec-flash" - or "mtd-ram". + used, if known, followed by either "cfi-flash", "jedec-flash", + "mtd-ram" or "mtd-rom". - reg : Address range(s) of the mtd chip(s) It's possible to (optionally) define multiple "reg" tuples so that non-identical chips can be described in one node. diff --git a/Documentation/devicetree/bindings/net/sti-dwmac.txt b/Documentation/devicetree/bindings/net/sti-dwmac.txt index 3dd3d0bf112f7fe530777be11763814f56685cb5..6762a6b5da7e91f7cf215a62c026885365b015f9 100644 --- a/Documentation/devicetree/bindings/net/sti-dwmac.txt +++ b/Documentation/devicetree/bindings/net/sti-dwmac.txt @@ -1,58 +1,65 @@ STMicroelectronics SoC DWMAC glue layer controller +This file documents differences between the core properties in +Documentation/devicetree/bindings/net/stmmac.txt +and what is needed on STi platforms to program the stmmac glue logic. + The device node has following properties. Required properties: - - compatible : Can be "st,stih415-dwmac", "st,stih416-dwmac" or - "st,stid127-dwmac". - - reg : Offset of the glue configuration register map in system + - compatible : Can be "st,stih415-dwmac", "st,stih416-dwmac", + "st,stih407-dwmac", "st,stid127-dwmac". + - reg : Offset of the glue configuration register map in system configuration regmap pointed by st,syscon property and size. - - - reg-names : Should be "sti-ethconf". - - - st,syscon : Should be phandle to system configuration node which + - st,syscon : Should be phandle to system configuration node which encompases this glue registers. + - st,gmac_en: this is to enable the gmac into a dedicated sysctl control + register available on STiH407 SoC. + - sti-ethconf: this is the gmac glue logic register to enable the GMAC, + select among the different modes and program the clk retiming. + - pinctrl-0: pin-control for all the MII mode supported. - - st,tx-retime-src: On STi Parts for Giga bit speeds, 125Mhz clocks can be - wired up in from different sources. One via TXCLK pin and other via CLK_125 - pin. This wiring is totally board dependent. However the retiming glue - logic should be configured accordingly. Possible values for this property - - "txclk" - if 125Mhz clock is wired up via txclk line. - "clk_125" - if 125Mhz clock is wired up via clk_125 line. - - This property is only valid for Giga bit setup( GMII, RGMII), and it is - un-used for non-giga bit (MII and RMII) setups. Also note that internal - clockgen can not generate stable 125Mhz clock. - - - st,ext-phyclk: This boolean property indicates who is generating the clock - for tx and rx. This property is only valid for RMII case where the clock can - be generated from the MAC or PHY. - - - clock-names: should be "sti-ethclk". - - clocks: Should point to ethernet clockgen which can generate phyclk. - +Optional properties: + - resets : phandle pointing to the system reset controller with correct + reset line index for ethernet reset. + - st,ext-phyclk: valid only for RMII where PHY can generate 50MHz clock or + MAC can generate it. + - st,tx-retime-src: This specifies which clk is wired up to the mac for + retimeing tx lines. This is totally board dependent and can take one of the + posssible values from "txclk", "clk_125" or "clkgen". + If not passed, the internal clock will be used by default. + - sti-ethclk: this is the phy clock. + - sti-clkconf: this is an extra sysconfig register, available in new SoCs, + to program the clk retiming. + - st,gmac_en: to enable the GMAC, this only is present in some SoCs; e.g. + STiH407. Example: -ethernet0: dwmac@fe810000 { - device_type = "network"; - compatible = "st,stih416-dwmac", "snps,dwmac", "snps,dwmac-3.710"; - reg = <0xfe810000 0x8000>, <0x8bc 0x4>; - reg-names = "stmmaceth", "sti-ethconf"; - interrupts = <0 133 0>, <0 134 0>, <0 135 0>; - interrupt-names = "macirq", "eth_wake_irq", "eth_lpi"; - phy-mode = "mii"; +ethernet0: dwmac@9630000 { + device_type = "network"; + status = "disabled"; + compatible = "st,stih407-dwmac", "snps,dwmac", "snps,dwmac-3.710"; + reg = <0x9630000 0x8000>, <0x80 0x4>; + reg-names = "stmmaceth", "sti-ethconf"; - st,syscon = <&syscfg_rear>; + st,syscon = <&syscfg_sbc_reg>; + st,gmac_en; + resets = <&softreset STIH407_ETH1_SOFTRESET>; + reset-names = "stmmaceth"; - snps,pbl = <32>; + interrupts = , + , + ; + interrupt-names = "macirq", "eth_wake_irq", "eth_lpi"; + + snps,pbl = <32>; snps,mixed-burst; - resets = <&softreset STIH416_ETH0_SOFTRESET>; - reset-names = "stmmaceth"; - pinctrl-0 = <&pinctrl_mii0>; - pinctrl-names = "default"; - clocks = <&CLK_S_GMAC0_PHY>; - clock-names = "stmmaceth"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rgmii1>; + + clock-names = "stmmaceth", "sti-ethclk"; + clocks = <&CLK_S_C0_FLEXGEN CLK_EXT2F_A9>, + <&CLK_S_C0_FLEXGEN CLK_ETH_PHY>; }; diff --git a/Documentation/devicetree/bindings/panel/auo,b101xtn01.txt b/Documentation/devicetree/bindings/panel/auo,b101xtn01.txt new file mode 100644 index 0000000000000000000000000000000000000000..889d511d66c91901e1ee0839fa4eaa93947fcca6 --- /dev/null +++ b/Documentation/devicetree/bindings/panel/auo,b101xtn01.txt @@ -0,0 +1,7 @@ +AU Optronics Corporation 10.1" WXGA TFT LCD panel + +Required properties: +- compatible: should be "auo,b101xtn01" + +This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory. diff --git a/Documentation/devicetree/bindings/power/reset/ltc2952-poweroff.txt b/Documentation/devicetree/bindings/power/reset/ltc2952-poweroff.txt new file mode 100644 index 0000000000000000000000000000000000000000..0c94c637f63b05ea05556138d44c199cd12bd68e --- /dev/null +++ b/Documentation/devicetree/bindings/power/reset/ltc2952-poweroff.txt @@ -0,0 +1,26 @@ +Binding for the LTC2952 PowerPath controller + +This chip is used to externally trigger a system shut down. Once the trigger has +been sent, the chips' watchdog has to be reset to gracefully shut down. +If the Linux systems decides to shut down it powers off the platform via the +kill signal. + +Required properties: + +- compatible: Must contain: "lltc,ltc2952" +- trigger-gpios: phandle + gpio-specifier for the GPIO connected to the + chip's trigger line +- watchdog-gpios: phandle + gpio-specifier for the GPIO connected to the + chip's watchdog line +- kill-gpios: phandle + gpio-specifier for the GPIO connected to the + chip's kill line + +Example: + +ltc2952 { + compatible = "lltc,ltc2952"; + + trigger-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; + watchdog-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; + kill-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>; +}; diff --git a/Documentation/devicetree/bindings/power/reset/st-reset.txt b/Documentation/devicetree/bindings/power/reset/st-reset.txt new file mode 100644 index 0000000000000000000000000000000000000000..809af54f02f35c79bb98fe0a9f0b9c66247a8ddc --- /dev/null +++ b/Documentation/devicetree/bindings/power/reset/st-reset.txt @@ -0,0 +1,11 @@ +*Device-Tree bindings for ST SW reset functionality + +Required properties: +- compatible: should be "st,-restart". +- st,syscfg: should be a phandle of the syscfg node. + +Example node: + restart { + compatible = "st,stih416-restart"; + st,syscfg = <&syscfg_sbc>; + }; diff --git a/Documentation/devicetree/bindings/power/reset/syscon-reboot.txt b/Documentation/devicetree/bindings/power/reset/syscon-reboot.txt new file mode 100644 index 0000000000000000000000000000000000000000..11906316b43d0860133efce69c70f5a3e5a52157 --- /dev/null +++ b/Documentation/devicetree/bindings/power/reset/syscon-reboot.txt @@ -0,0 +1,23 @@ +Generic SYSCON mapped register reset driver + +This is a generic reset driver using syscon to map the reset register. +The reset is generally performed with a write to the reset register +defined by the register map pointed by syscon reference plus the offset +with the mask defined in the reboot node. + +Required properties: +- compatible: should contain "syscon-reboot" +- regmap: this is phandle to the register map node +- offset: offset in the register map for the reboot register (in bytes) +- mask: the reset value written to the reboot register (32 bit access) + +Default will be little endian mode, 32 bit access only. + +Examples: + + reboot { + compatible = "syscon-reboot"; + regmap = <®mapnode>; + offset = <0x0>; + mask = <0x1>; + }; diff --git a/Documentation/devicetree/bindings/rtc/dallas,ds1339.txt b/Documentation/devicetree/bindings/rtc/dallas,ds1339.txt new file mode 100644 index 0000000000000000000000000000000000000000..916f57601a8ff44a4995eece753798eafe118063 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/dallas,ds1339.txt @@ -0,0 +1,18 @@ +* Dallas DS1339 I2C Serial Real-Time Clock + +Required properties: +- compatible: Should contain "dallas,ds1339". +- reg: I2C address for chip + +Optional properties: +- trickle-resistor-ohms : Selected resistor for trickle charger + Values usable for ds1339 are 250, 2000, 4000 + Should be given if trickle charger should be enabled +- trickle-diode-disable : Do not use internal trickle charger diode + Should be given if internal trickle charger diode should be disabled +Example: + ds1339: rtc@68 { + compatible = "dallas,ds1339"; + trickle-resistor-ohms = <250>; + reg = <0x68>; + }; diff --git a/Documentation/devicetree/bindings/rtc/s3c-rtc.txt b/Documentation/devicetree/bindings/rtc/s3c-rtc.txt index 7ac7259fe9ea9d61eb208064c364b0998221aa11..ab757b84daa7edab473d2ec907cb0da3386d0b0a 100644 --- a/Documentation/devicetree/bindings/rtc/s3c-rtc.txt +++ b/Documentation/devicetree/bindings/rtc/s3c-rtc.txt @@ -3,7 +3,10 @@ Required properties: - compatible: should be one of the following. * "samsung,s3c2410-rtc" - for controllers compatible with s3c2410 rtc. + * "samsung,s3c2416-rtc" - for controllers compatible with s3c2416 rtc. + * "samsung,s3c2443-rtc" - for controllers compatible with s3c2443 rtc. * "samsung,s3c6410-rtc" - for controllers compatible with s3c6410 rtc. + * "samsung,exynos3250-rtc" - for controllers compatible with exynos3250 rtc. - reg: physical base address of the controller and length of memory mapped region. - interrupts: Two interrupt numbers to the cpu should be specified. First diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index f67e3f84e8bc45077c51f68766eceba16215857f..723999d737445274728bedde163d3534ca62339c 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -92,6 +92,7 @@ maxim Maxim Integrated Products mediatek MediaTek Inc. micrel Micrel Inc. microchip Microchip Technology Inc. +mitsubishi Mitsubishi Electric Corporation mosaixtech Mosaix Technologies, Inc. moxa Moxa mpl MPL AG @@ -139,11 +140,13 @@ sitronix Sitronix Technology Corporation smsc Standard Microsystems Corporation snps Synopsys, Inc. solidrun SolidRun +sony Sony Corporation spansion Spansion Inc. st STMicroelectronics ste ST-Ericsson stericsson ST-Ericsson synology Synology, Inc. +thine THine Electronics, Inc. ti Texas Instruments tlm Trusted Logic Mobility toradex Toradex AG diff --git a/Documentation/devicetree/bindings/video/adi,adv7123.txt b/Documentation/devicetree/bindings/video/adi,adv7123.txt new file mode 100644 index 0000000000000000000000000000000000000000..a6b2b2b8f3d9e3a6b770f349d7e2f6dff91c68d6 --- /dev/null +++ b/Documentation/devicetree/bindings/video/adi,adv7123.txt @@ -0,0 +1,50 @@ +Analog Device ADV7123 Video DAC +------------------------------- + +The ADV7123 is a digital-to-analog converter that outputs VGA signals from a +parallel video input. + +Required properties: + +- compatible: Should be "adi,adv7123" + +Optional properties: + +- psave-gpios: Power save control GPIO + +Required nodes: + +The ADV7123 has two video ports. Their connections are modeled using the OF +graph bindings specified in Documentation/devicetree/bindings/graph.txt. + +- Video port 0 for DPI input +- Video port 1 for VGA output + + +Example +------- + + adv7123: encoder@0 { + compatible = "adi,adv7123"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + adv7123_in: endpoint@0 { + remote-endpoint = <&dpi_out>; + }; + }; + + port@1 { + reg = <1>; + + adv7123_out: endpoint@0 { + remote-endpoint = <&vga_connector_in>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/video/atmel,lcdc.txt b/Documentation/devicetree/bindings/video/atmel,lcdc.txt index 7d0c4a1ab81181c300bb746ba4611a6d173364be..f059dd0b3d286c3adc1e2b42571a2d48de4b641b 100644 --- a/Documentation/devicetree/bindings/video/atmel,lcdc.txt +++ b/Documentation/devicetree/bindings/video/atmel,lcdc.txt @@ -20,6 +20,9 @@ Required nodes: - default-mode: a videomode within the display with timing parameters as specified below. +Optional properties: +- lcd-supply: Regulator for LCD supply voltage. + Example: fb0: fb@0x00500000 { diff --git a/Documentation/devicetree/bindings/video/exynos_dsim.txt b/Documentation/devicetree/bindings/video/exynos_dsim.txt index 31036c667d541a8057111915afc5b6c7eb0d6e9f..e74243b4b317ff74728c50b09e66b5f9347f6d69 100644 --- a/Documentation/devicetree/bindings/video/exynos_dsim.txt +++ b/Documentation/devicetree/bindings/video/exynos_dsim.txt @@ -2,6 +2,7 @@ Exynos MIPI DSI Master Required properties: - compatible: value should be one of the following + "samsung,exynos3250-mipi-dsi" /* for Exynos3250/3472 SoCs */ "samsung,exynos4210-mipi-dsi" /* for Exynos4 SoCs */ "samsung,exynos5410-mipi-dsi" /* for Exynos5410/5420/5440 SoCs */ - reg: physical base address and length of the registers set for the device diff --git a/Documentation/devicetree/bindings/video/renesas,du.txt b/Documentation/devicetree/bindings/video/renesas,du.txt new file mode 100644 index 0000000000000000000000000000000000000000..5102830f276017135a05289e5c92bd87ae6e9d21 --- /dev/null +++ b/Documentation/devicetree/bindings/video/renesas,du.txt @@ -0,0 +1,84 @@ +* Renesas R-Car Display Unit (DU) + +Required Properties: + + - compatible: must be one of the following. + - "renesas,du-r8a7779" for R8A7779 (R-Car H1) compatible DU + - "renesas,du-r8a7790" for R8A7790 (R-Car H2) compatible DU + - "renesas,du-r8a7791" for R8A7791 (R-Car M2) compatible DU + + - reg: A list of base address and length of each memory resource, one for + each entry in the reg-names property. + - reg-names: Name of the memory resources. The DU requires one memory + resource for the DU core (named "du") and one memory resource for each + LVDS encoder (named "lvds.x" with "x" being the LVDS controller numerical + index). + + - interrupt-parent: phandle of the parent interrupt controller. + - interrupts: Interrupt specifiers for the DU interrupts. + + - clocks: A list of phandles + clock-specifier pairs, one for each entry in + the clock-names property. + - clock-names: Name of the clocks. This property is model-dependent. + - R8A7779 uses a single functional clock. The clock doesn't need to be + named. + - R8A7790 and R8A7791 use one functional clock per channel and one clock + per LVDS encoder. The functional clocks must be named "du.x" with "x" + being the channel numerical index. The LVDS clocks must be named + "lvds.x" with "x" being the LVDS encoder numerical index. + +Required nodes: + +The connections to the DU output video ports are modeled using the OF graph +bindings specified in Documentation/devicetree/bindings/graph.txt. + +The following table lists for each supported model the port number +corresponding to each DU output. + + Port 0 Port1 Port2 +----------------------------------------------------------------------------- + R8A7779 (H1) DPAD 0 DPAD 1 - + R8A7790 (H2) DPAD LVDS 0 LVDS 1 + R8A7791 (M2) DPAD LVDS 0 - + + +Example: R8A7790 (R-Car H2) DU + + du: du@feb00000 { + compatible = "renesas,du-r8a7790"; + reg = <0 0xfeb00000 0 0x70000>, + <0 0xfeb90000 0 0x1c>, + <0 0xfeb94000 0 0x1c>; + reg-names = "du", "lvds.0", "lvds.1"; + interrupt-parent = <&gic>; + interrupts = <0 256 IRQ_TYPE_LEVEL_HIGH>, + <0 268 IRQ_TYPE_LEVEL_HIGH>, + <0 269 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&mstp7_clks R8A7790_CLK_DU0>, + <&mstp7_clks R8A7790_CLK_DU1>, + <&mstp7_clks R8A7790_CLK_DU2>, + <&mstp7_clks R8A7790_CLK_LVDS0>, + <&mstp7_clks R8A7790_CLK_LVDS1>; + clock-names = "du.0", "du.1", "du.2", "lvds.0", "lvds.1"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + du_out_rgb: endpoint { + }; + }; + port@1 { + reg = <1>; + du_out_lvds0: endpoint { + }; + }; + port@2 { + reg = <2>; + du_out_lvds1: endpoint { + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/video/samsung-fimd.txt b/Documentation/devicetree/bindings/video/samsung-fimd.txt index ecc899b9817b23718a2e321ed58bc6a28209a750..4e6c77c85546790bbb8b85c6528e31287ac9e81a 100644 --- a/Documentation/devicetree/bindings/video/samsung-fimd.txt +++ b/Documentation/devicetree/bindings/video/samsung-fimd.txt @@ -9,6 +9,7 @@ Required properties: "samsung,s3c2443-fimd"; /* for S3C24XX SoCs */ "samsung,s3c6400-fimd"; /* for S3C64XX SoCs */ "samsung,s5pv210-fimd"; /* for S5PV210 SoC */ + "samsung,exynos3250-fimd"; /* for Exynos3250/3472 SoCs */ "samsung,exynos4210-fimd"; /* for Exynos4 SoCs */ "samsung,exynos5250-fimd"; /* for Exynos5 SoCs */ diff --git a/Documentation/devicetree/bindings/video/thine,thc63lvdm83d b/Documentation/devicetree/bindings/video/thine,thc63lvdm83d new file mode 100644 index 0000000000000000000000000000000000000000..527e236e9a2a126dcdd814e3b50c972a7145fe44 --- /dev/null +++ b/Documentation/devicetree/bindings/video/thine,thc63lvdm83d @@ -0,0 +1,50 @@ +THine Electronics THC63LVDM83D LVDS serializer +---------------------------------------------- + +The THC63LVDM83D is an LVDS serializer designed to support pixel data +transmission between a host and a flat panel. + +Required properties: + +- compatible: Should be "thine,thc63lvdm83d" + +Optional properties: + +- pwdn-gpios: Power down control GPIO + +Required nodes: + +The THC63LVDM83D has two video ports. Their connections are modeled using the +OFgraph bindings specified in Documentation/devicetree/bindings/graph.txt. + +- Video port 0 for CMOS/TTL input +- Video port 1 for LVDS output + + +Example +------- + + lvds_enc: encoder@0 { + compatible = "thine,thc63lvdm83d"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + lvds_enc_in: endpoint@0 { + remote-endpoint = <&rgb_out>; + }; + }; + + port@1 { + reg = <1>; + + lvds_enc_out: endpoint@0 { + remote-endpoint = <&panel_in>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/video/vga-connector.txt b/Documentation/devicetree/bindings/video/vga-connector.txt new file mode 100644 index 0000000000000000000000000000000000000000..c727f298e7ad087de6108f32a72a9ae06f519e8b --- /dev/null +++ b/Documentation/devicetree/bindings/video/vga-connector.txt @@ -0,0 +1,36 @@ +VGA Connector +============= + +Required properties: + +- compatible: "vga-connector" + +Optional properties: + +- label: a symbolic name for the connector corresponding to a hardware label +- ddc-i2c-bus: phandle to the I2C bus that is connected to VGA DDC + +Required nodes: + +The VGA connector internal connections are modeled using the OF graph bindings +specified in Documentation/devicetree/bindings/graph.txt. + +The VGA connector has a single port that must be connected to a video source +port. + + +Example +------- + +vga0: connector@0 { + compatible = "vga-connector"; + label = "vga"; + + ddc-i2c-bus = <&i2c3>; + + port { + vga_connector_in: endpoint { + remote-endpoint = <&adv7123_out>; + }; + }; +}; diff --git a/Documentation/dmaengine.txt b/Documentation/dmaengine.txt index 573e28ce97513c872ff80d870b5471c7e3181028..11fb87ff6cd09f17d19d972fd968a48de6b77ed1 100644 --- a/Documentation/dmaengine.txt +++ b/Documentation/dmaengine.txt @@ -98,7 +98,7 @@ The slave DMA usage consists of following steps: unsigned long flags); The peripheral driver is expected to have mapped the scatterlist for - the DMA operation prior to calling device_prep_slave_sg, and must + the DMA operation prior to calling dmaengine_prep_slave_sg(), and must keep the scatterlist mapped until the DMA operation has completed. The scatterlist must be mapped using the DMA struct device. If a mapping needs to be synchronized later, dma_sync_*_for_*() must be @@ -195,5 +195,5 @@ Further APIs: Note: Not all DMA engine drivers can return reliable information for a running DMA channel. It is recommended that DMA engine users - pause or stop (via dmaengine_terminate_all) the channel before + pause or stop (via dmaengine_terminate_all()) the channel before using this API. diff --git a/Documentation/filesystems/autofs4.txt b/Documentation/filesystems/autofs4.txt new file mode 100644 index 0000000000000000000000000000000000000000..39d02e19fb6288f79769f356d7759a997bf9461f --- /dev/null +++ b/Documentation/filesystems/autofs4.txt @@ -0,0 +1,520 @@ + + + + +autofs - how it works +===================== + +Purpose +------- + +The goal of autofs is to provide on-demand mounting and race free +automatic unmounting of various other filesystems. This provides two +key advantages: + +1. There is no need to delay boot until all filesystems that + might be needed are mounted. Processes that try to access those + slow filesystems might be delayed but other processes can + continue freely. This is particularly important for + network filesystems (e.g. NFS) or filesystems stored on + media with a media-changing robot. + +2. The names and locations of filesystems can be stored in + a remote database and can change at any time. The content + in that data base at the time of access will be used to provide + a target for the access. The interpretation of names in the + filesystem can even be programmatic rather than database-backed, + allowing wildcards for example, and can vary based on the user who + first accessed a name. + +Context +------- + +The "autofs4" filesystem module is only one part of an autofs system. +There also needs to be a user-space program which looks up names +and mounts filesystems. This will often be the "automount" program, +though other tools including "systemd" can make use of "autofs4". +This document describes only the kernel module and the interactions +required with any user-space program. Subsequent text refers to this +as the "automount daemon" or simply "the daemon". + +"autofs4" is a Linux kernel module with provides the "autofs" +filesystem type. Several "autofs" filesystems can be mounted and they +can each be managed separately, or all managed by the same daemon. + +Content +------- + +An autofs filesystem can contain 3 sorts of objects: directories, +symbolic links and mount traps. Mount traps are directories with +extra properties as described in the next section. + +Objects can only be created by the automount daemon: symlinks are +created with a regular `symlink` system call, while directories and +mount traps are created with `mkdir`. The determination of whether a +directory should be a mount trap or not is quite _ad hoc_, largely for +historical reasons, and is determined in part by the +*direct*/*indirect*/*offset* mount options, and the *maxproto* mount option. + +If neither the *direct* or *offset* mount options are given (so the +mount is considered to be *indirect*), then the root directory is +always a regular directory, otherwise it is a mount trap when it is +empty and a regular directory when not empty. Note that *direct* and +*offset* are treated identically so a concise summary is that the root +directory is a mount trap only if the filesystem is mounted *direct* +and the root is empty. + +Directories created in the root directory are mount traps only if the +filesystem is mounted *indirect* and they are empty. + +Directories further down the tree depend on the *maxproto* mount +option and particularly whether it is less than five or not. +When *maxproto* is five, no directories further down the +tree are ever mount traps, they are always regular directories. When +the *maxproto* is four (or three), these directories are mount traps +precisely when they are empty. + +So: non-empty (i.e. non-leaf) directories are never mount traps. Empty +directories are sometimes mount traps, and sometimes not depending on +where in the tree they are (root, top level, or lower), the *maxproto*, +and whether the mount was *indirect* or not. + +Mount Traps +--------------- + +A core element of the implementation of autofs is the Mount Traps +which are provided by the Linux VFS. Any directory provided by a +filesystem can be designated as a trap. This involves two separate +features that work together to allow autofs to do its job. + +**DCACHE_NEED_AUTOMOUNT** + +If a dentry has the DCACHE_NEED_AUTOMOUNT flag set (which gets set if +the inode has S_AUTOMOUNT set, or can be set directly) then it is +(potentially) a mount trap. Any access to this directory beyond a +"`stat`" will (normally) cause the `d_op->d_automount()` dentry operation +to be called. The task of this method is to find the filesystem that +should be mounted on the directory and to return it. The VFS is +responsible for actually mounting the root of this filesystem on the +directory. + +autofs doesn't find the filesystem itself but sends a message to the +automount daemon asking it to find and mount the filesystem. The +autofs `d_automount` method then waits for the daemon to report that +everything is ready. It will then return "`NULL`" indicating that the +mount has already happened. The VFS doesn't try to mount anything but +follows down the mount that is already there. + +This functionality is sufficient for some users of mount traps such +as NFS which creates traps so that mountpoints on the server can be +reflected on the client. However it is not sufficient for autofs. As +mounting onto a directory is considered to be "beyond a `stat`", the +automount daemon would not be able to mount a filesystem on the 'trap' +directory without some way to avoid getting caught in the trap. For +that purpose there is another flag. + +**DCACHE_MANAGE_TRANSIT** + +If a dentry has DCACHE_MANAGE_TRANSIT set then two very different but +related behaviors are invoked, both using the `d_op->d_manage()` +dentry operation. + +Firstly, before checking to see if any filesystem is mounted on the +directory, d_manage() will be called with the `rcu_walk` parameter set +to `false`. It may return one of three things: + +- A return value of zero indicates that there is nothing special + about this dentry and normal checks for mounts and automounts + should proceed. + + autofs normally returns zero, but first waits for any + expiry (automatic unmounting of the mounted filesystem) to + complete. This avoids races. + +- A return value of `-EISDIR` tells the VFS to ignore any mounts + on the directory and to not consider calling `->d_automount()`. + This effectively disables the **DCACHE_NEED_AUTOMOUNT** flag + causing the directory not be a mount trap after all. + + autofs returns this if it detects that the process performing the + lookup is the automount daemon and that the mount has been + requested but has not yet completed. How it determines this is + discussed later. This allows the automount daemon not to get + caught in the mount trap. + + There is a subtlety here. It is possible that a second autofs + filesystem can be mounted below the first and for both of them to + be managed by the same daemon. For the daemon to be able to mount + something on the second it must be able to "walk" down past the + first. This means that d_manage cannot *always* return -EISDIR for + the automount daemon. It must only return it when a mount has + been requested, but has not yet completed. + + `d_manage` also returns `-EISDIR` if the dentry shouldn't be a + mount trap, either because it is a symbolic link or because it is + not empty. + +- Any other negative value is treated as an error and returned + to the caller. + + autofs can return + + - -ENOENT if the automount daemon failed to mount anything, + - -ENOMEM if it ran out of memory, + - -EINTR if a signal arrived while waiting for expiry to + complete + - or any other error sent down by the automount daemon. + + +The second use case only occurs during an "RCU-walk" and so `rcu_walk` +will be set. + +An RCU-walk is a fast and lightweight process for walking down a +filename path (i.e. it is like running on tip-toes). RCU-walk cannot +cope with all situations so when it finds a difficulty it falls back +to "REF-walk", which is slower but more robust. + +RCU-walk will never call `->d_automount`; the filesystems must already +be mounted or RCU-walk cannot handle the path. +To determine if a mount-trap is safe for RCU-walk mode it calls +`->d_manage()` with `rcu_walk` set to `true`. + +In this case `d_manage()` must avoid blocking and should avoid taking +spinlocks if at all possible. Its sole purpose is to determine if it +would be safe to follow down into any mounted directory and the only +reason that it might not be is if an expiry of the mount is +underway. + +In the `rcu_walk` case, `d_manage()` cannot return -EISDIR to tell the +VFS that this is a directory that doesn't require d_automount. If +`rcu_walk` sees a dentry with DCACHE_NEED_AUTOMOUNT set but nothing +mounted, it *will* fall back to REF-walk. `d_manage()` cannot make the +VFS remain in RCU-walk mode, but can only tell it to get out of +RCU-walk mode by returning `-ECHILD`. + +So `d_manage()`, when called with `rcu_walk` set, should either return +-ECHILD if there is any reason to believe it is unsafe to end the +mounted filesystem, and otherwise should return 0. + +autofs will return `-ECHILD` if an expiry of the filesystem has been +initiated or is being considered, otherwise it returns 0. + + +Mountpoint expiry +----------------- + +The VFS has a mechansim for automatically expiring unused mounts, +much as it can expire any unused dentry information from the dcache. +This is guided by the MNT_SHRINKABLE flag. This only applies to +mounts that were created by `d_automount()` returning a filesystem to be +mounted. As autofs doesn't return such a filesystem but leaves the +mounting to the automount daemon, it must involve the automount daemon +in unmounting as well. This also means that autofs has more control +of expiry. + +The VFS also supports "expiry" of mounts using the MNT_EXPIRE flag to +the `umount` system call. Unmounting with MNT_EXPIRE will fail unless +a previous attempt had been made, and the filesystem has been inactive +and untouched since that previous attempt. autofs4 does not depend on +this but has its own internal tracking of whether filesystems were +recently used. This allows individual names in the autofs directory +to expire separately. + +With version 4 of the protocol, the automount daemon can try to +unmount any filesystems mounted on the autofs filesystem or remove any +symbolic links or empty directories any time it likes. If the unmount +or removal is successful the filesystem will be returned to the state +it was before the mount or creation, so that any access of the name +will trigger normal auto-mount processing. In particlar, `rmdir` and +`unlink` do not leave negative entries in the dcache as a normal +filesystem would, so an attempt to access a recently-removed object is +passed to autofs for handling. + +With version 5, this is not safe except for unmounting from top-level +directories. As lower-level directories are never mount traps, other +processes will see an empty directory as soon as the filesystem is +unmounted. So it is generally safest to use the autofs expiry +protocol described below. + +Normally the daemon only wants to remove entries which haven't been +used for a while. For this purpose autofs maintains a "`last_used`" +time stamp on each directory or symlink. For symlinks it genuinely +does record the last time the symlink was "used" or followed to find +out where it points to. For directories the field is a slight +misnomer. It actually records the last time that autofs checked if +the directory or one of its descendents was busy and found that it +was. This is just as useful and doesn't require updating the field so +often. + +The daemon is able to ask autofs if anything is due to be expired, +using an `ioctl` as discussed later. For a *direct* mount, autofs +considers if the entire mount-tree can be unmounted or not. For an +*indirect* mount, autofs considers each of the names in the top level +directory to determine if any of those can be unmounted and cleaned +up. + +There is an option with indirect mounts to consider each of the leaves +that has been mounted on instead of considering the top-level names. +This is intended for compatability with version 4 of autofs and should +be considered as deprecated. + +When autofs considers a directory it checks the `last_used` time and +compares it with the "timeout" value set when the filesystem was +mounted, though this check is ignored in some cases. It also checks if +the directory or anything below it is in use. For symbolic links, +only the `last_used` time is ever considered. + +If both appear to support expiring the directory or symlink, an action +is taken. + +There are two ways to ask autofs to consider expiry. The first is to +use the **AUTOFS_IOC_EXPIRE** ioctl. This only works for indirect +mounts. If it finds something in the root directory to expire it will +return the name of that thing. Once a name has been returned the +automount daemon needs to unmount any filesystems mounted below the +name normally. As described above, this is unsafe for non-toplevel +mounts in a version-5 autofs. For this reason the current `automountd` +does not use this ioctl. + +The second mechanism uses either the **AUTOFS_DEV_IOCTL_EXPIRE_CMD** or +the **AUTOFS_IOC_EXPIRE_MULTI** ioctl. This will work for both direct and +indirect mounts. If it selects an object to expire, it will notify +the daemon using the notification mechanism described below. This +will block until the daemon acknowledges the expiry notification. +This implies that the "`EXPIRE`" ioctl must be sent from a different +thread than the one which handles notification. + +While the ioctl is blocking, the entry is marked as "expiring" and +`d_manage` will block until the daemon affirms that the unmount has +completed (together with removing any directories that might have been +necessary), or has been aborted. + +Communicating with autofs: detecting the daemon +----------------------------------------------- + +There are several forms of communication between the automount daemon +and the filesystem. As we have already seen, the daemon can create and +remove directories and symlinks using normal filesystem operations. +autofs knows whether a process requesting some operation is the daemon +or not based on its process-group id number (see getpgid(1)). + +When an autofs filesystem it mounted the pgid of the mounting +processes is recorded unless the "pgrp=" option is given, in which +case that number is recorded instead. Any request arriving from a +process in that process group is considered to come from the daemon. +If the daemon ever has to be stopped and restarted a new pgid can be +provided through an ioctl as will be described below. + +Communicating with autofs: the event pipe +----------------------------------------- + +When an autofs filesystem is mounted, the 'write' end of a pipe must +be passed using the 'fd=' mount option. autofs will write +notification messages to this pipe for the daemon to respond to. +For version 5, the format of the message is: + + struct autofs_v5_packet { + int proto_version; /* Protocol version */ + int type; /* Type of packet */ + autofs_wqt_t wait_queue_token; + __u32 dev; + __u64 ino; + __u32 uid; + __u32 gid; + __u32 pid; + __u32 tgid; + __u32 len; + char name[NAME_MAX+1]; + }; + +where the type is one of + + autofs_ptype_missing_indirect + autofs_ptype_expire_indirect + autofs_ptype_missing_direct + autofs_ptype_expire_direct + +so messages can indicate that a name is missing (something tried to +access it but it isn't there) or that it has been selected for expiry. + +The pipe will be set to "packet mode" (equivalent to passing +`O_DIRECT`) to _pipe2(2)_ so that a read from the pipe will return at +most one packet, and any unread portion of a packet will be discarded. + +The `wait_queue_token` is a unique number which can identify a +particular request to be acknowledged. When a message is sent over +the pipe the affected dentry is marked as either "active" or +"expiring" and other accesses to it block until the message is +acknowledged using one of the ioctls below and the relevant +`wait_queue_token`. + +Communicating with autofs: root directory ioctls +------------------------------------------------ + +The root directory of an autofs filesystem will respond to a number of +ioctls. The process issuing the ioctl must have the CAP_SYS_ADMIN +capability, or must be the automount daemon. + +The available ioctl commands are: + +- **AUTOFS_IOC_READY**: a notification has been handled. The argument + to the ioctl command is the "wait_queue_token" number + corresponding to the notification being acknowledged. +- **AUTOFS_IOC_FAIL**: similar to above, but indicates failure with + the error code `ENOENT`. +- **AUTOFS_IOC_CATATONIC**: Causes the autofs to enter "catatonic" + mode meaning that it stops sending notifications to the daemon. + This mode is also entered if a write to the pipe fails. +- **AUTOFS_IOC_PROTOVER**: This returns the protocol version in use. +- **AUTOFS_IOC_PROTOSUBVER**: Returns the protocol sub-version which + is really a version number for the implementation. It is + currently 2. +- **AUTOFS_IOC_SETTIMEOUT**: This passes a pointer to an unsigned + long. The value is used to set the timeout for expiry, and + the current timeout value is stored back through the pointer. +- **AUTOFS_IOC_ASKUMOUNT**: Returns, in the pointed-to `int`, 1 if + the filesystem could be unmounted. This is only a hint as + the situation could change at any instant. This call can be + use to avoid a more expensive full unmount attempt. +- **AUTOFS_IOC_EXPIRE**: as described above, this asks if there is + anything suitable to expire. A pointer to a packet: + + struct autofs_packet_expire_multi { + int proto_version; /* Protocol version */ + int type; /* Type of packet */ + autofs_wqt_t wait_queue_token; + int len; + char name[NAME_MAX+1]; + }; + + is required. This is filled in with the name of something + that can be unmounted or removed. If nothing can be expired, + `errno` is set to `EAGAIN`. Even though a `wait_queue_token` + is present in the structure, no "wait queue" is established + and no acknowledgment is needed. +- **AUTOFS_IOC_EXPIRE_MULTI**: This is similar to + **AUTOFS_IOC_EXPIRE** except that it causes notification to be + sent to the daemon, and it blocks until the daemon acknowledges. + The argument is an integer which can contain two different flags. + + **AUTOFS_EXP_IMMEDIATE** causes `last_used` time to be ignored + and objects are expired if the are not in use. + + **AUTOFS_EXP_LEAVES** will select a leaf rather than a top-level + name to expire. This is only safe when *maxproto* is 4. + +Communicating with autofs: char-device ioctls +--------------------------------------------- + +It is not always possible to open the root of an autofs filesystem, +particularly a *direct* mounted filesystem. If the automount daemon +is restarted there is no way for it to regain control of existing +mounts using any of the above communication channels. To address this +need there is a "miscellaneous" character device (major 10, minor 235) +which can be used to communicate directly with the autofs filesystem. +It requires CAP_SYS_ADMIN for access. + +The `ioctl`s that can be used on this device are described in a separate +document `autofs4-mount-control.txt`, and are summarized briefly here. +Each ioctl is passed a pointer to an `autofs_dev_ioctl` structure: + + struct autofs_dev_ioctl { + __u32 ver_major; + __u32 ver_minor; + __u32 size; /* total size of data passed in + * including this struct */ + __s32 ioctlfd; /* automount command fd */ + + __u32 arg1; /* Command parameters */ + __u32 arg2; + + char path[0]; + }; + +For the **OPEN_MOUNT** and **IS_MOUNTPOINT** commands, the target +filesystem is identified by the `path`. All other commands identify +the filesystem by the `ioctlfd` which is a file descriptor open on the +root, and which can be returned by **OPEN_MOUNT**. + +The `ver_major` and `ver_minor` are in/out parameters which check that +the requested version is supported, and report the maximum version +that the kernel module can support. + +Commands are: + +- **AUTOFS_DEV_IOCTL_VERSION_CMD**: does nothing, except validate and + set version numbers. +- **AUTOFS_DEV_IOCTL_OPENMOUNT_CMD**: return an open file descriptor + on the root of an autofs filesystem. The filesystem is identified + by name and device number, which is stored in `arg1`. Device + numbers for existing filesystems can be found in + `/proc/self/mountinfo`. +- **AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD**: same as `close(ioctlfd)`. +- **AUTOFS_DEV_IOCTL_SETPIPEFD_CMD**: if the filesystem is in + catatonic mode, this can provide the write end of a new pipe + in `arg1` to re-establish communication with a daemon. The + process group of the calling process is used to identify the + daemon. +- **AUTOFS_DEV_IOCTL_REQUESTER_CMD**: `path` should be a + name within the filesystem that has been auto-mounted on. + arg1 is the dev number of the underlying autofs. On successful + return, `arg1` and `arg2` will be the UID and GID of the process + which triggered that mount. + +- **AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD**: Check if path is a + mountpoint of a particular type - see separate documentation for + details. + +- **AUTOFS_DEV_IOCTL_PROTOVER_CMD**: +- **AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD**: +- **AUTOFS_DEV_IOCTL_READY_CMD**: +- **AUTOFS_DEV_IOCTL_FAIL_CMD**: +- **AUTOFS_DEV_IOCTL_CATATONIC_CMD**: +- **AUTOFS_DEV_IOCTL_TIMEOUT_CMD**: +- **AUTOFS_DEV_IOCTL_EXPIRE_CMD**: +- **AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD**: These all have the same + function as the similarly named **AUTOFS_IOC** ioctls, except + that **FAIL** can be given an explicit error number in `arg1` + instead of assuming `ENOENT`, and this **EXPIRE** command + corresponds to **AUTOFS_IOC_EXPIRE_MULTI**. + +Catatonic mode +-------------- + +As mentioned, an autofs mount can enter "catatonic" mode. This +happens if a write to the notification pipe fails, or if it is +explicitly requested by an `ioctl`. + +When entering catatonic mode, the pipe is closed and any pending +notifications are acknowledged with the error `ENOENT`. + +Once in catatonic mode attempts to access non-existing names will +result in `ENOENT` while attempts to access existing directories will +be treated in the same way as if they came from the daemon, so mount +traps will not fire. + +When the filesystem is mounted a _uid_ and _gid_ can be given which +set the ownership of directories and symbolic links. When the +filesystem is in catatonic mode, any process with a matching UID can +create directories or symlinks in the root directory, but not in other +directories. + +Catatonic mode can only be left via the +**AUTOFS_DEV_IOCTL_OPENMOUNT_CMD** ioctl on the `/dev/autofs`. + +autofs, name spaces, and shared mounts +-------------------------------------- + +With bind mounts and name spaces it is possible for an autofs +filesystem to appear at multiple places in one or more filesystem +name spaces. For this to work sensibly, the autofs filesystem should +always be mounted "shared". e.g. + +> `mount --make-shared /autofs/mount/point` + +The automount daemon is only able to mange a single mount location for +an autofs filesystem and if mounts on that are not 'shared', other +locations will not behave as expected. In particular access to those +other locations will likely result in the `ELOOP` error + +> Too many levels of symbolic links diff --git a/Documentation/filesystems/ntfs.txt b/Documentation/filesystems/ntfs.txt index 61947facfc07cbcef53256d6afe0529cfc5bca77..553f10d03076f8abdada8090a9af17e376394b21 100644 --- a/Documentation/filesystems/ntfs.txt +++ b/Documentation/filesystems/ntfs.txt @@ -14,7 +14,6 @@ Table of contents - The Device-Mapper driver - The Software RAID / MD driver - Limitations when using the MD driver -- ChangeLog Overview @@ -450,270 +449,3 @@ number of sectors BEFORE attempting to use it. You have been warned! Even better is to simply use the Device-Mapper for linear raid and then you do not have this problem with odd numbers of sectors. - - -ChangeLog -========= - -2.1.30: - - Fix writev() (it kept writing the first segment over and over again - instead of moving onto subsequent segments). - - Fix crash in ntfs_mft_record_alloc() when mapping the new extent mft - record failed. -2.1.29: - - Fix a deadlock when mounting read-write. -2.1.28: - - Fix a deadlock. -2.1.27: - - Implement page migration support so the kernel can move memory used - by NTFS files and directories around for management purposes. - - Add support for writing to sparse files created with Windows XP SP2. - - Many minor improvements and bug fixes. -2.1.26: - - Implement support for sector sizes above 512 bytes (up to the maximum - supported by NTFS which is 4096 bytes). - - Enhance support for NTFS volumes which were supported by Windows but - not by Linux due to invalid attribute list attribute flags. - - A few minor updates and bug fixes. -2.1.25: - - Write support is now extended with write(2) being able to both - overwrite existing file data and to extend files. Also, if a write - to a sparse region occurs, write(2) will fill in the hole. Note, - mmap(2) based writes still do not support writing into holes or - writing beyond the initialized size. - - Write support has a new feature and that is that truncate(2) and - open(2) with O_TRUNC are now implemented thus files can be both made - smaller and larger. - - Note: Both write(2) and truncate(2)/open(2) with O_TRUNC still have - limitations in that they - - only provide limited support for highly fragmented files. - - only work on regular, i.e. uncompressed and unencrypted files. - - never create sparse files although this will change once directory - operations are implemented. - - Lots of bug fixes and enhancements across the board. -2.1.24: - - Support journals ($LogFile) which have been modified by chkdsk. This - means users can boot into Windows after we marked the volume dirty. - The Windows boot will run chkdsk and then reboot. The user can then - immediately boot into Linux rather than having to do a full Windows - boot first before rebooting into Linux and we will recognize such a - journal and empty it as it is clean by definition. - - Support journals ($LogFile) with only one restart page as well as - journals with two different restart pages. We sanity check both and - either use the only sane one or the more recent one of the two in the - case that both are valid. - - Lots of bug fixes and enhancements across the board. -2.1.23: - - Stamp the user space journal, aka transaction log, aka $UsnJrnl, if - it is present and active thus telling Windows and applications using - the transaction log that changes can have happened on the volume - which are not recorded in $UsnJrnl. - - Detect the case when Windows has been hibernated (suspended to disk) - and if this is the case do not allow (re)mounting read-write to - prevent data corruption when you boot back into the suspended - Windows session. - - Implement extension of resident files using the normal file write - code paths, i.e. most very small files can be extended to be a little - bit bigger but not by much. - - Add new mount option "disable_sparse". (See list of mount options - above for details.) - - Improve handling of ntfs volumes with errors and strange boot sectors - in particular. - - Fix various bugs including a nasty deadlock that appeared in recent - kernels (around 2.6.11-2.6.12 timeframe). -2.1.22: - - Improve handling of ntfs volumes with errors. - - Fix various bugs and race conditions. -2.1.21: - - Fix several race conditions and various other bugs. - - Many internal cleanups, code reorganization, optimizations, and mft - and index record writing code rewritten to fit in with the changes. - - Update Documentation/filesystems/ntfs.txt with instructions on how to - use the Device-Mapper driver with NTFS ftdisk/LDM raid. -2.1.20: - - Fix two stupid bugs introduced in 2.1.18 release. -2.1.19: - - Minor bugfix in handling of the default upcase table. - - Many internal cleanups and improvements. Many thanks to Linus - Torvalds and Al Viro for the help and advice with the sparse - annotations and cleanups. -2.1.18: - - Fix scheduling latencies at mount time. (Ingo Molnar) - - Fix endianness bug in a little traversed portion of the attribute - lookup code. -2.1.17: - - Fix bugs in mount time error code paths. -2.1.16: - - Implement access time updates (including mtime and ctime). - - Implement fsync(2), fdatasync(2), and msync(2) system calls. - - Enable the readv(2) and writev(2) system calls. - - Enable access via the asynchronous io (aio) API by adding support for - the aio_read(3) and aio_write(3) functions. -2.1.15: - - Invalidate quotas when (re)mounting read-write. - NOTE: This now only leave user space journalling on the side. (See - note for version 2.1.13, below.) -2.1.14: - - Fix an NFSd caused deadlock reported by several users. -2.1.13: - - Implement writing of inodes (access time updates are not implemented - yet so mounting with -o noatime,nodiratime is enforced). - - Enable writing out of resident files so you can now overwrite any - uncompressed, unencrypted, nonsparse file as long as you do not - change the file size. - - Add housekeeping of ntfs system files so that ntfsfix no longer needs - to be run after writing to an NTFS volume. - NOTE: This still leaves quota tracking and user space journalling on - the side but they should not cause data corruption. In the worst - case the charged quotas will be out of date ($Quota) and some - userspace applications might get confused due to the out of date - userspace journal ($UsnJrnl). -2.1.12: - - Fix the second fix to the decompression engine from the 2.1.9 release - and some further internals cleanups. -2.1.11: - - Driver internal cleanups. -2.1.10: - - Force read-only (re)mounting of volumes with unsupported volume - flags and various cleanups. -2.1.9: - - Fix two bugs in handling of corner cases in the decompression engine. -2.1.8: - - Read the $MFT mirror and compare it to the $MFT and if the two do not - match, force a read-only mount and do not allow read-write remounts. - - Read and parse the $LogFile journal and if it indicates that the - volume was not shutdown cleanly, force a read-only mount and do not - allow read-write remounts. If the $LogFile indicates a clean - shutdown and a read-write (re)mount is requested, empty $LogFile to - ensure that Windows cannot cause data corruption by replaying a stale - journal after Linux has written to the volume. - - Improve time handling so that the NTFS time is fully preserved when - converted to kernel time and only up to 99 nano-seconds are lost when - kernel time is converted to NTFS time. -2.1.7: - - Enable NFS exporting of mounted NTFS volumes. -2.1.6: - - Fix minor bug in handling of compressed directories that fixes the - erroneous "du" and "stat" output people reported. -2.1.5: - - Minor bug fix in attribute list attribute handling that fixes the - I/O errors on "ls" of certain fragmented files found by at least two - people running Windows XP. -2.1.4: - - Minor update allowing compilation with all gcc versions (well, the - ones the kernel can be compiled with anyway). -2.1.3: - - Major bug fixes for reading files and volumes in corner cases which - were being hit by Windows 2k/XP users. -2.1.2: - - Major bug fixes alleviating the hangs in statfs experienced by some - users. -2.1.1: - - Update handling of compressed files so people no longer get the - frequently reported warning messages about initialized_size != - data_size. -2.1.0: - - Add configuration option for developmental write support. - - Initial implementation of file overwriting. (Writes to resident files - are not written out to disk yet, so avoid writing to files smaller - than about 1kiB.) - - Intercept/abort changes in file size as they are not implemented yet. -2.0.25: - - Minor bugfixes in error code paths and small cleanups. -2.0.24: - - Small internal cleanups. - - Support for sendfile system call. (Christoph Hellwig) -2.0.23: - - Massive internal locking changes to mft record locking. Fixes - various race conditions and deadlocks. - - Fix ntfs over loopback for compressed files by adding an - optimization barrier. (gcc was screwing up otherwise ?) - Thanks go to Christoph Hellwig for pointing these two out: - - Remove now unused function fs/ntfs/malloc.h::vmalloc_nofs(). - - Fix ntfs_free() for ia64 and parisc. -2.0.22: - - Small internal cleanups. -2.0.21: - These only affect 32-bit architectures: - - Check for, and refuse to mount too large volumes (maximum is 2TiB). - - Check for, and refuse to open too large files and directories - (maximum is 16TiB). -2.0.20: - - Support non-resident directory index bitmaps. This means we now cope - with huge directories without problems. - - Fix a page leak that manifested itself in some cases when reading - directory contents. - - Internal cleanups. -2.0.19: - - Fix race condition and improvements in block i/o interface. - - Optimization when reading compressed files. -2.0.18: - - Fix race condition in reading of compressed files. -2.0.17: - - Cleanups and optimizations. -2.0.16: - - Fix stupid bug introduced in 2.0.15 in new attribute inode API. - - Big internal cleanup replacing the mftbmp access hacks by using the - new attribute inode API instead. -2.0.15: - - Bug fix in parsing of remount options. - - Internal changes implementing attribute (fake) inodes allowing all - attribute i/o to go via the page cache and to use all the normal - vfs/mm functionality. -2.0.14: - - Internal changes improving run list merging code and minor locking - change to not rely on BKL in ntfs_statfs(). -2.0.13: - - Internal changes towards using iget5_locked() in preparation for - fake inodes and small cleanups to ntfs_volume structure. -2.0.12: - - Internal cleanups in address space operations made possible by the - changes introduced in the previous release. -2.0.11: - - Internal updates and cleanups introducing the first step towards - fake inode based attribute i/o. -2.0.10: - - Microsoft says that the maximum number of inodes is 2^32 - 1. Update - the driver accordingly to only use 32-bits to store inode numbers on - 32-bit architectures. This improves the speed of the driver a little. -2.0.9: - - Change decompression engine to use a single buffer. This should not - affect performance except perhaps on the most heavy i/o on SMP - systems when accessing multiple compressed files from multiple - devices simultaneously. - - Minor updates and cleanups. -2.0.8: - - Remove now obsolete show_inodes and posix mount option(s). - - Restore show_sys_files mount option. - - Add new mount option case_sensitive, to determine if the driver - treats file names as case sensitive or not. - - Mostly drop support for short file names (for backwards compatibility - we only support accessing files via their short file name if one - exists). - - Fix dcache aliasing issues wrt short/long file names. - - Cleanups and minor fixes. -2.0.7: - - Just cleanups. -2.0.6: - - Major bugfix to make compatible with other kernel changes. This fixes - the hangs/oopses on umount. - - Locking cleanup in directory operations (remove BKL usage). -2.0.5: - - Major buffer overflow bug fix. - - Minor cleanups and updates for kernel 2.5.12. -2.0.4: - - Cleanups and updates for kernel 2.5.11. -2.0.3: - - Small bug fixes, cleanups, and performance improvements. -2.0.2: - - Use default fmask of 0177 so that files are no executable by default. - If you want owner executable files, just use fmask=0077. - - Update for kernel 2.5.9 but preserve backwards compatibility with - kernel 2.5.7. - - Minor bug fixes, cleanups, and updates. -2.0.1: - - Minor updates, primarily set the executable bit by default on files - so they can be executed. -2.0.0: - - Started ChangeLog. - diff --git a/Documentation/i2c/busses/i2c-i801 b/Documentation/i2c/busses/i2c-i801 index e9c803ea306d73adcf9cbe8407cfa1eef42fe150..793c83dac738381504bcd82812d56224d5873a7d 100644 --- a/Documentation/i2c/busses/i2c-i801 +++ b/Documentation/i2c/busses/i2c-i801 @@ -28,6 +28,7 @@ Supported adapters: * Intel Wildcat Point (PCH) * Intel Wildcat Point-LP (PCH) * Intel BayTrail (SOC) + * Intel Sunrise Point-H (PCH) Datasheets: Publicly available at the Intel website On Intel Patsburg and later chipsets, both the normal host SMBus controller diff --git a/Documentation/kbuild/makefiles.txt b/Documentation/kbuild/makefiles.txt index 764f5991a3fc74c32d9f218be85b5c977ea4efde..a311db829e9bb6a819bc293404d65d5c3584a741 100644 --- a/Documentation/kbuild/makefiles.txt +++ b/Documentation/kbuild/makefiles.txt @@ -174,7 +174,7 @@ more details, with real examples. --- 3.3 Loadable module goals - obj-m - $(obj-m) specify object files which are built as loadable + $(obj-m) specifies object files which are built as loadable kernel modules. A module may be built from one source file or several source @@ -277,7 +277,7 @@ more details, with real examples. down in the ext2 directory. Kbuild only uses this information to decide that it needs to visit the directory, it is the Makefile in the subdirectory that - specifies what is modules and what is built-in. + specifies what is modular and what is built-in. It is good practice to use a CONFIG_ variable when assigning directory names. This allows kbuild to totally skip the directory if the @@ -403,7 +403,7 @@ more details, with real examples. echoing information to user in a rule is often a good practice but when execution "make -s" one does not expect to see any output except for warnings/errors. - To support this kbuild define $(kecho) which will echo out the + To support this kbuild defines $(kecho) which will echo out the text following $(kecho) to stdout except if "make -s" is used. Example: @@ -417,7 +417,7 @@ more details, with real examples. The kernel may be built with several different versions of $(CC), each supporting a unique set of features and options. - kbuild provide basic support to check for valid options for $(CC). + kbuild provides basic support to check for valid options for $(CC). $(CC) is usually the gcc compiler, but other alternatives are available. @@ -456,8 +456,8 @@ more details, with real examples. Note: as-instr-option uses KBUILD_AFLAGS for $(AS) options cc-option - cc-option is used to check if $(CC) supports a given option, and not - supported to use an optional second option. + cc-option is used to check if $(CC) supports a given option, and if + not supported to use an optional second option. Example: #arch/x86/Makefile @@ -557,8 +557,8 @@ more details, with real examples. false ; \ fi - In this example for a specific GCC version the build will error out explaining - to the user why it stops. + In this example for a specific GCC version the build will error out + explaining to the user why it stops. cc-cross-prefix cc-cross-prefix is used to check if there exists a $(CC) in path with @@ -656,7 +656,7 @@ Both possibilities are described in the following. In the example above the executable is composed of the C++ file qconf.cc - identified by $(qconf-cxxobjs). - If qconf is composed by a mixture of .c and .cc files, then an + If qconf is composed of a mixture of .c and .cc files, then an additional line can be used to identify this. Example: @@ -733,7 +733,7 @@ Both possibilities are described in the following. hostprogs-$(CONFIG_KALLSYMS) += kallsyms Kbuild knows about both 'y' for built-in and 'm' for module. - So if a config symbol evaluate to 'm', kbuild will still build + So if a config symbol evaluates to 'm', kbuild will still build the binary. In other words, Kbuild handles hostprogs-m exactly like hostprogs-y. But only hostprogs-y is recommended to be used when no CONFIG symbols are involved. @@ -754,8 +754,8 @@ Additional files can be specified in kbuild makefiles by use of $(clean-files). #drivers/pci/Makefile clean-files := devlist.h classlist.h -When executing "make clean", the two files "devlist.h classlist.h" will -be deleted. Kbuild will assume files to be in same relative directory as the +When executing "make clean", the two files "devlist.h classlist.h" will be +deleted. Kbuild will assume files to be in the same relative directory as the Makefile except if an absolute path is specified (path starting with '/'). To delete a directory hierarchy use: @@ -786,7 +786,7 @@ is not sufficient this sometimes needs to be explicit. The above assignment instructs kbuild to descend down in the directory compressed/ when "make clean" is executed. -To support the clean infrastructure in the Makefiles that builds the +To support the clean infrastructure in the Makefiles that build the final bootimage there is an optional target named archclean: Example: @@ -818,17 +818,16 @@ a few targets. When kbuild executes, the following steps are followed (roughly): 1) Configuration of the kernel => produce .config 2) Store kernel version in include/linux/version.h -3) Symlink include/asm to include/asm-$(ARCH) -4) Updating all other prerequisites to the target prepare: +3) Updating all other prerequisites to the target prepare: - Additional prerequisites are specified in arch/$(ARCH)/Makefile -5) Recursively descend down in all directories listed in +4) Recursively descend down in all directories listed in init-* core* drivers-* net-* libs-* and build all targets. - The values of the above variables are expanded in arch/$(ARCH)/Makefile. -6) All object files are then linked and the resulting file vmlinux is +5) All object files are then linked and the resulting file vmlinux is located at the root of the obj tree. The very first objects linked are listed in head-y, assigned by arch/$(ARCH)/Makefile. -7) Finally, the architecture-specific part does any required post processing +6) Finally, the architecture-specific part does any required post processing and builds the final bootimage. - This includes building boot records - Preparing initrd images and the like @@ -927,7 +926,7 @@ When kbuild executes, the following steps are followed (roughly): KBUILD_AFLAGS_MODULE Options for $(AS) when building modules - $(KBUILD_AFLAGS_MODULE) is used to add arch specific options that + $(KBUILD_AFLAGS_MODULE) is used to add arch-specific options that are used for $(AS). From commandline AFLAGS_MODULE shall be used (see kbuild.txt). @@ -938,13 +937,13 @@ When kbuild executes, the following steps are followed (roughly): KBUILD_CFLAGS_MODULE Options for $(CC) when building modules - $(KBUILD_CFLAGS_MODULE) is used to add arch specific options that + $(KBUILD_CFLAGS_MODULE) is used to add arch-specific options that are used for $(CC). From commandline CFLAGS_MODULE shall be used (see kbuild.txt). KBUILD_LDFLAGS_MODULE Options for $(LD) when linking modules - $(KBUILD_LDFLAGS_MODULE) is used to add arch specific options + $(KBUILD_LDFLAGS_MODULE) is used to add arch-specific options used when linking modules. This is often a linker script. From commandline LDFLAGS_MODULE shall be used (see kbuild.txt). @@ -1066,7 +1065,7 @@ When kbuild executes, the following steps are followed (roughly): extra-y - extra-y specify additional targets created in the current + extra-y specifies additional targets created in the current directory, in addition to any targets specified by obj-*. Listing all targets in extra-y is required for two purposes: @@ -1093,7 +1092,7 @@ When kbuild executes, the following steps are followed (roughly): Usage: target: source(s) FORCE - $(call if_changed,ld/objcopy/gzip) + $(call if_changed,ld/objcopy/gzip/...) When the rule is evaluated, it is checked to see if any files need an update, or the command line has changed since the last @@ -1111,7 +1110,7 @@ When kbuild executes, the following steps are followed (roughly): significant; for instance, the below will fail (note the extra space after the comma): target: source(s) FORCE - #WRONG!# $(call if_changed, ld/objcopy/gzip) + #WRONG!# $(call if_changed, ld/objcopy/gzip/...) ld Link target. Often, LDFLAGS_$@ is used to set specific options to ld. @@ -1142,8 +1141,8 @@ When kbuild executes, the following steps are followed (roughly): 2) delete target during make clean The ": %: %.o" part of the prerequisite is a shorthand that - free us from listing the setup.o and bootsect.o files. - Note: It is a common mistake to forget the "target :=" assignment, + frees us from listing the setup.o and bootsect.o files. + Note: It is a common mistake to forget the "targets :=" assignment, resulting in the target file being recompiled for no obvious reason. @@ -1164,29 +1163,6 @@ When kbuild executes, the following steps are followed (roughly): clean-files += *.dtb DTC_FLAGS ?= -p 1024 - dtc_cpp - This is just like dtc as describe above, except that the C pre- - processor is invoked upon the .dtsp file before compiling the result - with dtc. - - In order for build dependencies to work, all files compiled using - dtc_cpp must use the C pre-processor's #include functionality and not - dtc's /include/ functionality. - - Using the C pre-processor allows use of #define to create named - constants. In turn, the #defines will typically appear in a header - file, which may be shared with regular C code. Since the dtc language - represents a data structure rather than code in C syntax, similar - restrictions are placed on a header file included by a device tree - file as for a header file included by an assembly language file. - In particular, the C pre-processor is passed -x assembler-with-cpp, - which sets macro __ASSEMBLY__. __DTS__ is also set. These allow header - files to restrict their content to that compatible with device tree - source. - - A central rule exists to create $(obj)/%.dtb from $(src)/%.dtsp; - architecture Makefiles do no need to explicitly write out that rule. - --- 6.8 Custom kbuild commands When kbuild is executing with KBUILD_VERBOSE=0, then only a shorthand @@ -1237,11 +1213,11 @@ When kbuild executes, the following steps are followed (roughly): When building the *.lds target, kbuild uses the variables: KBUILD_CPPFLAGS : Set in top-level Makefile cppflags-y : May be set in the kbuild makefile - CPPFLAGS_$(@F) : Target specific flags. + CPPFLAGS_$(@F) : Target-specific flags. Note that the full filename is used in this assignment. - The kbuild infrastructure for *lds file are used in several + The kbuild infrastructure for *lds files is used in several architecture-specific files. --- 6.10 Generic header files @@ -1254,11 +1230,11 @@ When kbuild executes, the following steps are followed (roughly): === 7 Kbuild syntax for exported headers -The kernel include a set of headers that is exported to userspace. +The kernel includes a set of headers that is exported to userspace. Many headers can be exported as-is but other headers require a minimal pre-processing before they are ready for user-space. The pre-processing does: -- drop kernel specific annotations +- drop kernel-specific annotations - drop include of compiler.h - drop all sections that are kernel internal (guarded by ifdef __KERNEL__) @@ -1268,7 +1244,7 @@ See subsequent chapter for the syntax of the Kbuild file. --- 7.1 header-y - header-y specify header files to be exported. + header-y specifies header files to be exported. Example: #include/linux/Kbuild @@ -1278,7 +1254,7 @@ See subsequent chapter for the syntax of the Kbuild file. The convention is to list one file per line and preferably in alphabetic order. - header-y also specify which subdirectories to visit. + header-y also specifies which subdirectories to visit. A subdirectory is identified by a trailing '/' which can be seen in the example above for the usb subdirectory. @@ -1296,9 +1272,9 @@ See subsequent chapter for the syntax of the Kbuild file. --- 7.3 destination-y - When an architecture have a set of exported headers that needs to be + When an architecture has a set of exported headers that needs to be exported to a different directory destination-y is used. - destination-y specify the destination directory for all exported + destination-y specifies the destination directory for all exported headers in the file where it is present. Example: @@ -1391,9 +1367,9 @@ The top Makefile exports the following variables: INSTALL_MOD_STRIP - If this variable is specified, will cause modules to be stripped + If this variable is specified, it will cause modules to be stripped after they are installed. If INSTALL_MOD_STRIP is '1', then the - default option --strip-debug will be used. Otherwise, + default option --strip-debug will be used. Otherwise, the INSTALL_MOD_STRIP value will be used as the option(s) to the strip command. diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 04e9f5505faa8fa4133c198a946cdefab241824d..7dbe5ec9d9cd08afac13797e2adac291fb703eec 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -605,11 +605,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted. See Documentation/s390/CommonIO for details. clk_ignore_unused [CLK] - Keep all clocks already enabled by bootloader on, - even if no driver has claimed them. This is useful - for debug and development, but should not be - needed on a platform with proper driver support. - For more information, see Documentation/clk.txt. + Prevents the clock framework from automatically gating + clocks that have not been explicitly enabled by a Linux + device driver but are enabled in hardware at reset or + by the bootloader/firmware. Note that this does not + force such clocks to be always-on nor does it reserve + those clocks in any way. This parameter is useful for + debug and development, but should not be needed on a + platform with proper driver support. For more + information, see Documentation/clk.txt. clock= [BUGS=X86-32, HW] gettimeofday clocksource override. [Deprecated] @@ -1256,7 +1260,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. i8042.noloop [HW] Disable the AUX Loopback command while probing for the AUX port i8042.nomux [HW] Don't check presence of an active multiplexing - controller + controller. Default: true. i8042.nopnp [HW] Don't use ACPIPnP / PnPBIOS to discover KBD/AUX controllers i8042.notimeout [HW] Ignore timeout condition signalled by controller diff --git a/Documentation/kprobes.txt b/Documentation/kprobes.txt index 4bbeca8483ed339f7efd5b6314da77f9b4a99f0d..4227ec2e3ab21c13dd6e07342e264e257005a2a0 100644 --- a/Documentation/kprobes.txt +++ b/Documentation/kprobes.txt @@ -300,6 +300,7 @@ architectures: - arm - ppc - mips +- s390 3. Configuring Kprobes diff --git a/Documentation/power/charger-manager.txt b/Documentation/power/charger-manager.txt index b4f7f4b23f648e3e129bf3b8d31b68e17e004ee2..9ff1105e58d64fd7df6f0f17f63fc0e7f14bcc7d 100644 --- a/Documentation/power/charger-manager.txt +++ b/Documentation/power/charger-manager.txt @@ -29,7 +29,7 @@ Charger Manager supports the following: While the battery is being charged and the system is in suspend-to-RAM, we may need to monitor the battery health by looking at the ambient or battery temperature. We can accomplish this by waking up the system - periodically. However, such a method wakes up devices unncessary for + periodically. However, such a method wakes up devices unnecessarily for monitoring the battery health and tasks, and user processes that are supposed to be kept suspended. That, in turn, incurs unnecessary power consumption and slow down charging process. Or even, such peak power diff --git a/Documentation/power/power_supply_class.txt b/Documentation/power/power_supply_class.txt index 48cff881cb8a13aac48c4455ac44d44f685eaf46..82dacc06e355c1624ca66e5668448f1a767dbfa9 100644 --- a/Documentation/power/power_supply_class.txt +++ b/Documentation/power/power_supply_class.txt @@ -101,6 +101,10 @@ VOLTAGE_MAX, VOLTAGE_MIN - same as _DESIGN voltage values except that these ones should be used if hardware could only guess (measure and retain) the thresholds of a given power supply. +VOLTAGE_BOOT - Reports the voltage measured during boot + +CURRENT_BOOT - Reports the current measured during boot + CHARGE_FULL_DESIGN, CHARGE_EMPTY_DESIGN - design charge values, when battery considered full/empty. @@ -123,6 +127,8 @@ the current drawn from a charging source. CHARGE_TERM_CURRENT - Charge termination current used to detect the end of charge condition. +CALIBRATE - battery or coulomb counter calibration status + CONSTANT_CHARGE_VOLTAGE - constant charge voltage programmed by charger. CONSTANT_CHARGE_VOLTAGE_MAX - maximum charge voltage supported by the power supply object. diff --git a/Documentation/printk-formats.txt b/Documentation/printk-formats.txt index b4498218c4744213bbe57609c7532fa4bd64bd54..5a615c14f75da79133db306179ccdd355bb0b4c4 100644 --- a/Documentation/printk-formats.txt +++ b/Documentation/printk-formats.txt @@ -70,6 +70,38 @@ DMA addresses types dma_addr_t: For printing a dma_addr_t type which can vary based on build options, regardless of the width of the CPU data path. Passed by reference. +Raw buffer as an escaped string: + + %*pE[achnops] + + For printing raw buffer as an escaped string. For the following buffer + + 1b 62 20 5c 43 07 22 90 0d 5d + + few examples show how the conversion would be done (the result string + without surrounding quotes): + + %*pE "\eb \C\a"\220\r]" + %*pEhp "\x1bb \C\x07"\x90\x0d]" + %*pEa "\e\142\040\\\103\a\042\220\r\135" + + The conversion rules are applied according to an optional combination + of flags (see string_escape_mem() kernel documentation for the + details): + a - ESCAPE_ANY + c - ESCAPE_SPECIAL + h - ESCAPE_HEX + n - ESCAPE_NULL + o - ESCAPE_OCTAL + p - ESCAPE_NP + s - ESCAPE_SPACE + By default ESCAPE_ANY_NP is used. + + ESCAPE_ANY_NP is the sane choice for many cases, in particularly for + printing SSIDs. + + If field width is omitted the 1 byte only will be escaped. + Raw buffer as a hex string: %*ph 00 01 02 ... 3f %*phC 00:01:02: ... :3f diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index f79eb96663790f1c116f6a44d706af048bdedc88..57baff5bdb806b3b725bdc7859c385fbdc1cac54 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -190,6 +190,8 @@ core_pattern is used to specify a core dumpfile pattern name. %% output one '%' %p pid %P global pid (init PID namespace) + %i tid + %I global tid (init PID namespace) %u uid %g gid %d dump mode, matches PR_SET_DUMPABLE and diff --git a/MAINTAINERS b/MAINTAINERS index f413abff3807d72a7a1b1f574169936245038872..a20df9bf8ab0efc22f5ced80f39ff2b4f429a89c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -564,7 +564,7 @@ L: linux-alpha@vger.kernel.org F: arch/alpha/ ALTERA TRIPLE SPEED ETHERNET DRIVER -M: Vince Bridgers +M: Vince Bridgers L: netdev@vger.kernel.org L: nios2-dev@lists.rocketboards.org (moderated for non-subscribers) S: Maintained @@ -1088,33 +1088,33 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/TEXAS INSTRUMENT KEYSTONE ARCHITECTURE -M: Santosh Shilimkar +M: Santosh Shilimkar L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-keystone/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/ssantosh/linux-keystone.git ARM/TEXAS INSTRUMENT KEYSTONE CLOCK FRAMEWORK -M: Santosh Shilimkar +M: Santosh Shilimkar L: linux-kernel@vger.kernel.org S: Maintained F: drivers/clk/keystone/ ARM/TEXAS INSTRUMENT KEYSTONE ClOCKSOURCE -M: Santosh Shilimkar +M: Santosh Shilimkar L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) L: linux-kernel@vger.kernel.org S: Maintained F: drivers/clocksource/timer-keystone.c ARM/TEXAS INSTRUMENT KEYSTONE RESET DRIVER -M: Santosh Shilimkar +M: Santosh Shilimkar L: linux-kernel@vger.kernel.org S: Maintained F: drivers/power/reset/keystone-reset.c ARM/TEXAS INSTRUMENT AEMIF/EMIF DRIVERS -M: Santosh Shilimkar +M: Santosh Shilimkar L: linux-kernel@vger.kernel.org S: Maintained F: drivers/memory/*emif* @@ -1338,8 +1338,7 @@ ARM/SAMSUNG MOBILE MACHINE SUPPORT M: Kyungmin Park L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -F: arch/arm/mach-s5pv210/mach-aquila.c -F: arch/arm/mach-s5pv210/mach-goni.c +F: arch/arm/mach-s5pv210/ ARM/SAMSUNG S5P SERIES 2D GRAPHICS ACCELERATION (G2D) SUPPORT M: Kyungmin Park @@ -1550,6 +1549,7 @@ T: git git://git.xilinx.com/linux-xlnx.git S: Supported F: arch/arm/mach-zynq/ F: drivers/cpuidle/cpuidle-zynq.c +F: drivers/block/xsysace.c N: zynq N: xilinx F: drivers/clocksource/cadence_ttc_timer.c @@ -1738,6 +1738,12 @@ M: Nicolas Ferre S: Supported F: drivers/net/ethernet/cadence/ +ATMEL NAND DRIVER +M: Josh Wu +L: linux-mtd@lists.infradead.org +S: Supported +F: drivers/mtd/nand/atmel_nand* + ATMEL SPI DRIVER M: Nicolas Ferre S: Supported @@ -2056,17 +2062,14 @@ F: arch/arm/configs/bcm_defconfig F: drivers/mmc/host/sdhci-bcm-kona.c F: drivers/clocksource/bcm_kona_timer.c -BROADCOM BCM2835 ARM ARCHICTURE +BROADCOM BCM2835 ARM ARCHITECTURE M: Stephen Warren L: linux-rpi-kernel@lists.infradead.org (moderated for non-subscribers) T: git git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-rpi.git S: Maintained -F: arch/arm/mach-bcm/board_bcm2835.c -F: arch/arm/boot/dts/bcm2835* -F: arch/arm/configs/bcm2835_defconfig -F: drivers/*/*bcm2835* +N: bcm2835 -BROADCOM BCM5301X ARM ARCHICTURE +BROADCOM BCM5301X ARM ARCHITECTURE M: Hauke Mehrtens L: linux-arm-kernel@lists.infradead.org S: Maintained @@ -3048,7 +3051,7 @@ M: Sumit Semwal S: Maintained L: linux-media@vger.kernel.org L: dri-devel@lists.freedesktop.org -L: linaro-mm-sig@lists.linaro.org +L: linaro-mm-sig@lists.linaro.org (moderated for non-subscribers) F: drivers/dma-buf/ F: include/linux/dma-buf* F: include/linux/reservation.h @@ -3079,14 +3082,13 @@ S: Supported F: drivers/acpi/dock.c DOCUMENTATION -M: Jiri Kosina +M: Jonathan Corbet L: linux-doc@vger.kernel.org S: Maintained F: Documentation/ X: Documentation/ABI/ X: Documentation/devicetree/ X: Documentation/[a-z][a-z]_[A-Z][A-Z]/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/doc.git DOUBLETALK DRIVER M: "James R. Van Zandt" @@ -3158,7 +3160,7 @@ F: include/drm/drm_panel.h F: Documentation/devicetree/bindings/panel/ INTEL DRM DRIVERS (excluding Poulsbo, Moorestown and derivative chipsets) -M: Daniel Vetter +M: Daniel Vetter M: Jani Nikula L: intel-gfx@lists.freedesktop.org L: dri-devel@lists.freedesktop.org @@ -4297,9 +4299,8 @@ S: Maintained F: drivers/media/dvb-frontends/hd29l2* HEWLETT-PACKARD SMART2 RAID DRIVER -M: Chirag Kantharia L: iss_storagedev@hp.com -S: Maintained +S: Orphan F: Documentation/blockdev/cpqarray.txt F: drivers/block/cpqarray.* @@ -5108,6 +5109,7 @@ F: include/scsi/*iscsi* ISCSI EXTENSIONS FOR RDMA (ISER) INITIATOR M: Or Gerlitz +M: Sagi Grimberg M: Roi Dayan L: linux-rdma@vger.kernel.org S: Supported @@ -5300,6 +5302,13 @@ F: include/linux/lockd/ F: include/linux/sunrpc/ F: include/uapi/linux/sunrpc/ +KERNEL SELFTEST FRAMEWORK +M: Shuah Khan +L: linux-api@vger.kernel.org +T: git git://git.kernel.org/pub/scm/shuah/linux-kselftest +S: Maintained +F: tools/testing/selftests + KERNEL VIRTUAL MACHINE (KVM) M: Gleb Natapov M: Paolo Bonzini @@ -5746,11 +5755,8 @@ T: git git://github.com/linux-test-project/ltp.git S: Maintained M32R ARCHITECTURE -M: Hirokazu Takata -L: linux-m32r@ml.linux-m32r.org (moderated for non-subscribers) -L: linux-m32r-ja@ml.linux-m32r.org (in Japanese) W: http://www.linux-m32r.org/ -S: Maintained +S: Orphan F: arch/m32r/ M68K ARCHITECTURE @@ -5984,6 +5990,7 @@ L: linux-mtd@lists.infradead.org W: http://www.linux-mtd.infradead.org/ Q: http://patchwork.ozlabs.org/project/linux-mtd/list/ T: git git://git.infradead.org/linux-mtd.git +T: git git://git.infradead.org/l2-mtd.git S: Maintained F: drivers/mtd/ F: include/linux/mtd/ @@ -6001,6 +6008,15 @@ S: Supported F: drivers/mcb/ F: include/linux/mcb.h +MEN F21BMC (Board Management Controller) +M: Andreas Werner +S: Supported +F: drivers/mfd/menf21bmc.c +F: drivers/watchdog/menf21bmc_wdt.c +F: drivers/leds/leds-menf21bmc.c +F: drivers/hwmon/menf21bmc_hwmon.c +F: Documentation/hwmon/menf21bmc + METAG ARCHITECTURE M: James Hogan L: linux-metag@vger.kernel.org @@ -6668,7 +6684,7 @@ F: arch/arm/*omap*/usb* OMAP GPIO DRIVER M: Javier Martinez Canillas -M: Santosh Shilimkar +M: Santosh Shilimkar M: Kevin Hilman L: linux-omap@vger.kernel.org S: Maintained @@ -7974,7 +7990,6 @@ S: Supported F: drivers/mfd/sec*.c F: drivers/regulator/s2m*.c F: drivers/regulator/s5m*.c -F: drivers/rtc/rtc-sec.c F: include/linux/mfd/samsung/ SAMSUNG S5P/EXYNOS4 SOC SERIES CAMERA SUBSYSTEM DRIVERS @@ -8045,7 +8060,7 @@ SYNOPSYS DESIGNWARE DMAC DRIVER M: Viresh Kumar M: Andy Shevchenko S: Maintained -F: include/linux/dw_dmac.h +F: include/linux/platform_data/dma-dw.h F: drivers/dma/dw/ SYNOPSYS DESIGNWARE MMC/SD/SDIO DRIVER @@ -9256,7 +9271,7 @@ F: drivers/mmc/host/tifm_sd.c F: include/linux/tifm.h TI KEYSTONE MULTICORE NAVIGATOR DRIVERS -M: Santosh Shilimkar +M: Santosh Shilimkar L: linux-kernel@vger.kernel.org L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained @@ -10315,10 +10330,6 @@ M: John Linn S: Maintained F: drivers/net/ethernet/xilinx/xilinx_axienet* -XILINX SYSTEMACE DRIVER -S: Orphan -F: drivers/block/xsysace.c - XILINX UARTLITE SERIAL DRIVER M: Peter Korsgaard L: linux-serial@vger.kernel.org diff --git a/Makefile b/Makefile index 6127ca66ed981600ca738a43b3d558be43415c56..05d67af376c5ed73f911deabf130817a8521cdeb 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 3 -PATCHLEVEL = 17 +PATCHLEVEL = 18 SUBLEVEL = 0 -EXTRAVERSION = +EXTRAVERSION = -rc1 NAME = Shuffling Zombie Juror # *DOCUMENTATION* @@ -10,11 +10,9 @@ NAME = Shuffling Zombie Juror # Comments in this file are targeted only to the developer, do not # expect to learn how to build the kernel reading this file. -# Do not: -# o use make's built-in rules and variables -# (this increases performance and avoids hard-to-debug behaviour); -# o print "Entering directory ..."; -MAKEFLAGS += -rR --no-print-directory +# Do not use make's built-in rules and variables +# (this increases performance and avoids hard-to-debug behaviour); +MAKEFLAGS += -rR # Avoid funny character set dependencies unexport LC_ALL @@ -97,34 +95,6 @@ endif export quiet Q KBUILD_VERBOSE -# Call a source code checker (by default, "sparse") as part of the -# C compilation. -# -# Use 'make C=1' to enable checking of only re-compiled files. -# Use 'make C=2' to enable checking of *all* source files, regardless -# of whether they are re-compiled or not. -# -# See the file "Documentation/sparse.txt" for more details, including -# where to get the "sparse" utility. - -ifeq ("$(origin C)", "command line") - KBUILD_CHECKSRC = $(C) -endif -ifndef KBUILD_CHECKSRC - KBUILD_CHECKSRC = 0 -endif - -# Use make M=dir to specify directory of external module to build -# Old syntax make ... SUBDIRS=$PWD is still supported -# Setting the environment variable KBUILD_EXTMOD take precedence -ifdef SUBDIRS - KBUILD_EXTMOD ?= $(SUBDIRS) -endif - -ifeq ("$(origin M)", "command line") - KBUILD_EXTMOD := $(M) -endif - # kbuild supports saving output files in a separate directory. # To locate output files in a separate directory two syntaxes are supported. # In both cases the working directory must be the root of the kernel src. @@ -140,7 +110,6 @@ endif # The O= assignment takes precedence over the KBUILD_OUTPUT environment # variable. - # KBUILD_SRC is set on invocation of make in OBJ directory # KBUILD_SRC is not intended to be used by the regular user (for now) ifeq ($(KBUILD_SRC),) @@ -172,17 +141,9 @@ PHONY += $(MAKECMDGOALS) sub-make $(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make @: -# Fake the "Entering directory" message once, so that IDEs/editors are -# able to understand relative filenames. - echodir := @echo - quiet_echodir := @echo -silent_echodir := @: sub-make: FORCE - $($(quiet)echodir) "make[1]: Entering directory \`$(KBUILD_OUTPUT)'" - $(if $(KBUILD_VERBOSE:1=),@)$(MAKE) -C $(KBUILD_OUTPUT) \ - KBUILD_SRC=$(CURDIR) \ - KBUILD_EXTMOD="$(KBUILD_EXTMOD)" -f $(CURDIR)/Makefile \ - $(filter-out _all sub-make,$(MAKECMDGOALS)) + $(Q)$(MAKE) -C $(KBUILD_OUTPUT) KBUILD_SRC=$(CURDIR) \ + -f $(CURDIR)/Makefile $(filter-out _all sub-make,$(MAKECMDGOALS)) # Leave processing to above invocation of make skip-makefile := 1 @@ -192,6 +153,39 @@ endif # ifeq ($(KBUILD_SRC),) # We process the rest of the Makefile if this is the final invocation of make ifeq ($(skip-makefile),) +# Do not print "Entering directory ...", +# but we want to display it when entering to the output directory +# so that IDEs/editors are able to understand relative filenames. +MAKEFLAGS += --no-print-directory + +# Call a source code checker (by default, "sparse") as part of the +# C compilation. +# +# Use 'make C=1' to enable checking of only re-compiled files. +# Use 'make C=2' to enable checking of *all* source files, regardless +# of whether they are re-compiled or not. +# +# See the file "Documentation/sparse.txt" for more details, including +# where to get the "sparse" utility. + +ifeq ("$(origin C)", "command line") + KBUILD_CHECKSRC = $(C) +endif +ifndef KBUILD_CHECKSRC + KBUILD_CHECKSRC = 0 +endif + +# Use make M=dir to specify directory of external module to build +# Old syntax make ... SUBDIRS=$PWD is still supported +# Setting the environment variable KBUILD_EXTMOD take precedence +ifdef SUBDIRS + KBUILD_EXTMOD ?= $(SUBDIRS) +endif + +ifeq ("$(origin M)", "command line") + KBUILD_EXTMOD := $(M) +endif + # If building an external module we do not care about the all: rule # but instead _all depend on modules PHONY += all @@ -889,9 +883,7 @@ vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \ $(net-y) $(net-m) $(libs-y) $(libs-m))) vmlinux-alldirs := $(sort $(vmlinux-dirs) $(patsubst %/,%,$(filter %/, \ - $(init-n) $(init-) \ - $(core-n) $(core-) $(drivers-n) $(drivers-) \ - $(net-n) $(net-) $(libs-n) $(libs-)))) + $(init-) $(core-) $(drivers-) $(net-) $(libs-)))) init-y := $(patsubst %/, %/built-in.o, $(init-y)) core-y := $(patsubst %/, %/built-in.o, $(core-y)) @@ -1591,7 +1583,7 @@ endif # Shorthand for $(Q)$(MAKE) -f scripts/Makefile.clean obj=dir # Usage: # $(Q)$(MAKE) $(clean)=dir -clean := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.clean obj +clean := -f $(srctree)/scripts/Makefile.clean obj endif # skip-makefile diff --git a/arch/alpha/include/asm/syscall.h b/arch/alpha/include/asm/syscall.h new file mode 100644 index 0000000000000000000000000000000000000000..88d28eb2a5662f9daf8f1527cd19863b1fcc1b5d --- /dev/null +++ b/arch/alpha/include/asm/syscall.h @@ -0,0 +1,11 @@ +#ifndef _ASM_ALPHA_SYSCALL_H +#define _ASM_ALPHA_SYSCALL_H + +#include + +static inline int syscall_get_arch(void) +{ + return AUDIT_ARCH_ALPHA; +} + +#endif /* _ASM_ALPHA_SYSCALL_H */ diff --git a/arch/alpha/kernel/perf_event.c b/arch/alpha/kernel/perf_event.c index c52e7f0ee5f6084bd2c8068a552659f97edd9a8d..5c218aa3f3df425e1220e3d1c607b53d5c77dd55 100644 --- a/arch/alpha/kernel/perf_event.c +++ b/arch/alpha/kernel/perf_event.c @@ -431,7 +431,7 @@ static void maybe_change_configuration(struct cpu_hw_events *cpuc) */ static int alpha_pmu_add(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc = &event->hw; int n0; int ret; @@ -483,7 +483,7 @@ static int alpha_pmu_add(struct perf_event *event, int flags) */ static void alpha_pmu_del(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc = &event->hw; unsigned long irq_flags; int j; @@ -531,7 +531,7 @@ static void alpha_pmu_read(struct perf_event *event) static void alpha_pmu_stop(struct perf_event *event, int flags) { struct hw_perf_event *hwc = &event->hw; - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); if (!(hwc->state & PERF_HES_STOPPED)) { cpuc->idx_mask &= ~(1UL<idx); @@ -551,7 +551,7 @@ static void alpha_pmu_stop(struct perf_event *event, int flags) static void alpha_pmu_start(struct perf_event *event, int flags) { struct hw_perf_event *hwc = &event->hw; - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED))) return; @@ -724,7 +724,7 @@ static int alpha_pmu_event_init(struct perf_event *event) */ static void alpha_pmu_enable(struct pmu *pmu) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); if (cpuc->enabled) return; @@ -750,7 +750,7 @@ static void alpha_pmu_enable(struct pmu *pmu) static void alpha_pmu_disable(struct pmu *pmu) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); if (!cpuc->enabled) return; @@ -814,8 +814,8 @@ static void alpha_perf_event_irq_handler(unsigned long la_ptr, struct hw_perf_event *hwc; int idx, j; - __get_cpu_var(irq_pmi_count)++; - cpuc = &__get_cpu_var(cpu_hw_events); + __this_cpu_inc(irq_pmi_count); + cpuc = this_cpu_ptr(&cpu_hw_events); /* Completely counting through the PMC's period to trigger a new PMC * overflow interrupt while in this interrupt routine is utterly diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c index 86d835157b546f08f7eff0cc58a5f3dccbc5b359..d9ee81769899fb8652046857d2fa3eaabd1bacc1 100644 --- a/arch/alpha/kernel/ptrace.c +++ b/arch/alpha/kernel/ptrace.c @@ -321,7 +321,7 @@ asmlinkage unsigned long syscall_trace_enter(void) if (test_thread_flag(TIF_SYSCALL_TRACE) && tracehook_report_syscall_entry(current_pt_regs())) ret = -1UL; - audit_syscall_entry(AUDIT_ARCH_ALPHA, regs->r0, regs->r16, regs->r17, regs->r18, regs->r19); + audit_syscall_entry(regs->r0, regs->r16, regs->r17, regs->r18, regs->r19); return ret ?: current_pt_regs()->r0; } diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c index ee39cee8064caa4e930a06eb2fa35457ad6d390e..643a9dcdf0934f333a31cda7ce9bba45f7aaff28 100644 --- a/arch/alpha/kernel/time.c +++ b/arch/alpha/kernel/time.c @@ -56,9 +56,9 @@ unsigned long est_cycle_freq; DEFINE_PER_CPU(u8, irq_work_pending); -#define set_irq_work_pending_flag() __get_cpu_var(irq_work_pending) = 1 -#define test_irq_work_pending() __get_cpu_var(irq_work_pending) -#define clear_irq_work_pending() __get_cpu_var(irq_work_pending) = 0 +#define set_irq_work_pending_flag() __this_cpu_write(irq_work_pending, 1) +#define test_irq_work_pending() __this_cpu_read(irq_work_pending) +#define clear_irq_work_pending() __this_cpu_write(irq_work_pending, 0) void arch_irq_work_raise(void) { diff --git a/arch/arm/Makefile b/arch/arm/Makefile index dceb0441b1a6de111eb5cc3e9b96e1bbb5e69f34..034a94904d69e5578e3792408368ebc6c1d08a77 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -50,8 +50,6 @@ AS += -EL LD += -EL endif -comma = , - # This selects which instruction set is used. # Note that GCC does not numerically define an architecture version # macro, but instead defines a whole series of macros which makes diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 7c80af9068976b11ceb0fcef749adf2738d80273..38c89cafa1ab8ea3ee682b8ccb0c02af8739f785 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -354,13 +354,15 @@ dtb-$(CONFIG_ARCH_ORION5X) += orion5x-lacie-d2-network.dtb \ orion5x-rd88f5182-nas.dtb dtb-$(CONFIG_ARCH_PRIMA2) += prima2-evb.dtb dtb-$(CONFIG_ARCH_QCOM) += \ + qcom-apq8064-cm-qs600.dtb \ qcom-apq8064-ifc6410.dtb \ qcom-apq8074-dragonboard.dtb \ qcom-apq8084-ifc6540.dtb \ qcom-apq8084-mtp.dtb \ qcom-ipq8064-ap148.dtb \ qcom-msm8660-surf.dtb \ - qcom-msm8960-cdp.dtb + qcom-msm8960-cdp.dtb \ + qcom-msm8974-sony-xperia-honami.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += \ rk3066a-bqcurie2.dtb \ rk3188-radxarock.dtb \ diff --git a/arch/arm/boot/dts/exynos3250.dtsi b/arch/arm/boot/dts/exynos3250.dtsi index 1d52de6370d58a65022b0bc59bf2f07516066308..693a3275606f2ab4c25e7647c5606ac42e88139d 100644 --- a/arch/arm/boot/dts/exynos3250.dtsi +++ b/arch/arm/boot/dts/exynos3250.dtsi @@ -132,6 +132,12 @@ reg = <0x10020000 0x4000>; }; + mipi_phy: video-phy@10020710 { + compatible = "samsung,s5pv210-mipi-video-phy"; + reg = <0x10020710 8>; + #phy-cells = <1>; + }; + pd_cam: cam-power-domain@10023C00 { compatible = "samsung,exynos4210-pd"; reg = <0x10023C00 0x20>; @@ -163,8 +169,14 @@ #clock-cells = <1>; }; + cmu_dmc: clock-controller@105C0000 { + compatible = "samsung,exynos3250-cmu-dmc"; + reg = <0x105C0000 0x2000>; + #clock-cells = <1>; + }; + rtc: rtc@10070000 { - compatible = "samsung,s3c6410-rtc"; + compatible = "samsung,exynos3250-rtc"; reg = <0x10070000 0x100>; interrupts = <0 73 0>, <0 74 0>; status = "disabled"; @@ -216,6 +228,33 @@ interrupts = <0 240 0>; }; + fimd: fimd@11c00000 { + compatible = "samsung,exynos3250-fimd"; + reg = <0x11c00000 0x30000>; + interrupt-names = "fifo", "vsync", "lcd_sys"; + interrupts = <0 84 0>, <0 85 0>, <0 86 0>; + clocks = <&cmu CLK_SCLK_FIMD0>, <&cmu CLK_FIMD0>; + clock-names = "sclk_fimd", "fimd"; + samsung,power-domain = <&pd_lcd0>; + samsung,sysreg = <&sys_reg>; + status = "disabled"; + }; + + dsi_0: dsi@11C80000 { + compatible = "samsung,exynos3250-mipi-dsi"; + reg = <0x11C80000 0x10000>; + interrupts = <0 83 0>; + samsung,phy-type = <0>; + samsung,power-domain = <&pd_lcd0>; + phys = <&mipi_phy 1>; + phy-names = "dsim"; + clocks = <&cmu CLK_DSIM0>, <&cmu CLK_SCLK_MIPI0>; + clock-names = "bus_clk", "pll_clk"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + mshc_0: mshc@12510000 { compatible = "samsung,exynos5250-dw-mshc"; reg = <0x12510000 0x1000>; diff --git a/arch/arm/boot/dts/exynos4x12-pinctrl.dtsi b/arch/arm/boot/dts/exynos4x12-pinctrl.dtsi index 99b26df8dbc7e381effb4f17d86984ba633ce783..0865a2e33f970d32e740de3a80b7481cc8764c5d 100644 --- a/arch/arm/boot/dts/exynos4x12-pinctrl.dtsi +++ b/arch/arm/boot/dts/exynos4x12-pinctrl.dtsi @@ -675,7 +675,7 @@ sd4_bus8: sd4-bus-width8 { samsung,pins = "gpk1-3", "gpk1-4", "gpk1-5", "gpk1-6"; samsung,pin-function = <4>; - samsung,pin-pud = <4>; + samsung,pin-pud = <3>; samsung,pin-drv = <3>; }; diff --git a/arch/arm/boot/dts/exynos5420-arndale-octa.dts b/arch/arm/boot/dts/exynos5420-arndale-octa.dts index 4f2df61c1cfc6fde03a39968d6c8d5666ba5e834..aa7a7d727a7e80033df0cddb0b9cce8596d48ddc 100644 --- a/arch/arm/boot/dts/exynos5420-arndale-octa.dts +++ b/arch/arm/boot/dts/exynos5420-arndale-octa.dts @@ -368,3 +368,7 @@ }; }; }; + +&usbdrd_dwc3_1 { + dr_mode = "host"; +}; diff --git a/arch/arm/boot/dts/exynos5420-peach-pit.dts b/arch/arm/boot/dts/exynos5420-peach-pit.dts index 9a233828539cfbce55a3ea81ccc504a0a7ad434d..82cdb74484cc09c7d45a95b036d148c12f8144e7 100644 --- a/arch/arm/boot/dts/exynos5420-peach-pit.dts +++ b/arch/arm/boot/dts/exynos5420-peach-pit.dts @@ -838,6 +838,14 @@ status = "okay"; }; +&usbdrd_dwc3_0 { + dr_mode = "host"; +}; + +&usbdrd_dwc3_1 { + dr_mode = "host"; +}; + &usbdrd_phy0 { vbus-supply = <&usb300_vbus_reg>; }; diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi index bfe056d9148ce8d476b30c9fb54981154fefab5f..8617a031cbc06bf670e42f167056fe6c061b0d63 100644 --- a/arch/arm/boot/dts/exynos5420.dtsi +++ b/arch/arm/boot/dts/exynos5420.dtsi @@ -815,7 +815,7 @@ #size-cells = <1>; ranges; - dwc3 { + usbdrd_dwc3_0: dwc3 { compatible = "snps,dwc3"; reg = <0x12000000 0x10000>; interrupts = <0 72 0>; @@ -841,7 +841,7 @@ #size-cells = <1>; ranges; - dwc3 { + usbdrd_dwc3_1: dwc3 { compatible = "snps,dwc3"; reg = <0x12400000 0x10000>; interrupts = <0 73 0>; diff --git a/arch/arm/boot/dts/exynos5800-peach-pi.dts b/arch/arm/boot/dts/exynos5800-peach-pi.dts index 1d31c813255849087233fda0e52c7dc61d1faa10..7bb1c8dd42dd9a8c5d3c3196249787a39b0442b9 100644 --- a/arch/arm/boot/dts/exynos5800-peach-pi.dts +++ b/arch/arm/boot/dts/exynos5800-peach-pi.dts @@ -826,6 +826,14 @@ status = "okay"; }; +&usbdrd_dwc3_0 { + dr_mode = "host"; +}; + +&usbdrd_dwc3_1 { + dr_mode = "host"; +}; + &usbdrd_phy0 { vbus-supply = <&usb300_vbus_reg>; }; diff --git a/arch/arm/boot/dts/omap3-n900.dts b/arch/arm/boot/dts/omap3-n900.dts index 9b0494a8ab454f21b67060416b698ed10d710f64..739fcf29c6439af97b6a8bca100db0b3fd082e88 100644 --- a/arch/arm/boot/dts/omap3-n900.dts +++ b/arch/arm/boot/dts/omap3-n900.dts @@ -659,6 +659,9 @@ ethernet@gpmc { compatible = "smsc,lan91c94"; + + status = "disabled"; + interrupt-parent = <&gpio2>; interrupts = <22 IRQ_TYPE_LEVEL_HIGH>; /* gpio54 */ reg = <1 0x300 0xf>; /* 16 byte IO range at offset 0x300 */ diff --git a/arch/arm/boot/dts/pxa27x.dtsi b/arch/arm/boot/dts/pxa27x.dtsi index a7054694598594cb91aefb77b791c6f3f1706018..80fc5d7e9ef9685cc04fb8771f6205aaeaf5932a 100644 --- a/arch/arm/boot/dts/pxa27x.dtsi +++ b/arch/arm/boot/dts/pxa27x.dtsi @@ -1,5 +1,6 @@ /* The pxa3xx skeleton simply augments the 2xx version */ -/include/ "pxa2xx.dtsi" +#include "pxa2xx.dtsi" +#include "dt-bindings/clock/pxa2xx-clock.h" / { model = "Marvell PXA27x familiy SoC"; @@ -35,4 +36,21 @@ #pwm-cells = <1>; }; }; + + clocks { + /* + * The muxing of external clocks/internal dividers for osc* clock + * sources has been hidden under the carpet by now. + */ + #address-cells = <1>; + #size-cells = <1>; + ranges; + + pxa2xx_clks: pxa2xx_clks@41300004 { + compatible = "marvell,pxa-clocks"; + #clock-cells = <1>; + status = "okay"; + }; + }; + }; diff --git a/arch/arm/boot/dts/qcom-apq8064-cm-qs600.dts b/arch/arm/boot/dts/qcom-apq8064-cm-qs600.dts new file mode 100644 index 0000000000000000000000000000000000000000..5d75666f7f6c955e8ed34201e0c9811a64c66e3f --- /dev/null +++ b/arch/arm/boot/dts/qcom-apq8064-cm-qs600.dts @@ -0,0 +1,59 @@ +#include "qcom-apq8064-v2.0.dtsi" + +/ { + model = "CompuLab CM-QS600"; + compatible = "qcom,apq8064-cm-qs600", "qcom,apq8064"; + + soc { + pinctrl@800000 { + i2c1_pins: i2c1 { + mux { + pins = "gpio20", "gpio21"; + function = "gsbi1"; + }; + }; + }; + + gsbi@12440000 { + status = "okay"; + qcom,mode = ; + + i2c@12460000 { + status = "okay"; + clock-frequency = <200000>; + pinctrl-0 = <&i2c1_pins>; + pinctrl-names = "default"; + + eeprom: eeprom@50 { + compatible = "24c02"; + reg = <0x50>; + pagesize = <32>; + }; + }; + }; + + gsbi@16600000 { + status = "ok"; + qcom,mode = ; + serial@16640000 { + status = "ok"; + }; + }; + + amba { + /* eMMC */ + sdcc1: sdcc@12400000 { + status = "okay"; + }; + + /* External micro SD card */ + sdcc3: sdcc@12180000 { + status = "okay"; + }; + /* WLAN */ + sdcc4: sdcc@121c0000 { + status = "okay"; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts index 95e64955fb8e2aed02a886e218e3a58bf326c899..55b2910efd872170ca19a99d867be4a34dd60794 100644 --- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts @@ -81,5 +81,13 @@ }; }; }; + + sata-phy@1b400000 { + status = "ok"; + }; + + sata@29000000 { + status = "ok"; + }; }; }; diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi index 244f857f0e6f63a8408b22b499c017d0abf7ac05..63b2146f563b541e4994697af5ee1bbb41a4abd1 100644 --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi @@ -234,6 +234,39 @@ }; }; + sata_phy: sata-phy@1b400000 { + compatible = "qcom,ipq806x-sata-phy"; + reg = <0x1b400000 0x200>; + + clocks = <&gcc SATA_PHY_CFG_CLK>; + clock-names = "cfg"; + + #phy-cells = <0>; + status = "disabled"; + }; + + sata@29000000 { + compatible = "qcom,ipq806x-ahci", "generic-ahci"; + reg = <0x29000000 0x180>; + + interrupts = <0 209 0x0>; + + clocks = <&gcc SFAB_SATA_S_H_CLK>, + <&gcc SATA_H_CLK>, + <&gcc SATA_A_CLK>, + <&gcc SATA_RXOOB_CLK>, + <&gcc SATA_PMALIVE_CLK>; + clock-names = "slave_face", "iface", "core", + "rxoob", "pmalive"; + + assigned-clocks = <&gcc SATA_RXOOB_CLK>, <&gcc SATA_PMALIVE_CLK>; + assigned-clock-rates = <100000000>, <100000000>; + + phys = <&sata_phy>; + phy-names = "sata-phy"; + status = "disabled"; + }; + qcom,ssbi@500000 { compatible = "qcom,ssbi"; reg = <0x00500000 0x1000>; diff --git a/arch/arm/boot/dts/qcom-msm8974-sony-xperia-honami.dts b/arch/arm/boot/dts/qcom-msm8974-sony-xperia-honami.dts new file mode 100644 index 0000000000000000000000000000000000000000..cccc21b7c8fde929c3c118dde9c46be740179609 --- /dev/null +++ b/arch/arm/boot/dts/qcom-msm8974-sony-xperia-honami.dts @@ -0,0 +1,17 @@ +#include "qcom-msm8974.dtsi" + +/ { + model = "Sony Xperia Z1"; + compatible = "sony,xperia-honami", "qcom,msm8974"; + + memory@0 { + reg = <0 0x40000000>, <0x40000000 0x40000000>; + device_type = "memory"; + }; +}; + +&soc { + serial@f991e000 { + status = "ok"; + }; +}; diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi index d73a2287b37ab7affcb826c2ad864d8eefdb0c59..531272c0e526aec013c22ec6a7d6a3a044f88467 100644 --- a/arch/arm/boot/dts/sun5i-a10s.dtsi +++ b/arch/arm/boot/dts/sun5i-a10s.dtsi @@ -287,7 +287,7 @@ mbus_clk: clk@01c2015c { #clock-cells = <0>; - compatible = "allwinner,sun4i-a10-mod0-clk"; + compatible = "allwinner,sun5i-a13-mbus-clk"; reg = <0x01c2015c 0x4>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clock-output-names = "mbus"; diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi index c4b5d7825b9f78c89c0e088f07d77bc60e98e813..b131068f4f351ca92e7df07d63641cec143c5fc2 100644 --- a/arch/arm/boot/dts/sun5i-a13.dtsi +++ b/arch/arm/boot/dts/sun5i-a13.dtsi @@ -285,7 +285,7 @@ mbus_clk: clk@01c2015c { #clock-cells = <0>; - compatible = "allwinner,sun4i-a10-mod0-clk"; + compatible = "allwinner,sun5i-a13-mbus-clk"; reg = <0x01c2015c 0x4>; clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clock-output-names = "mbus"; diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index a96b9946506949d89e839310ac4980cd61111a17..82097c905c48cb5e99387db684c113fb6d37cb94 100644 --- a/arch/arm/boot/dts/sun7i-a20.dtsi +++ b/arch/arm/boot/dts/sun7i-a20.dtsi @@ -382,7 +382,7 @@ mbus_clk: clk@01c2015c { #clock-cells = <0>; - compatible = "allwinner,sun4i-a10-mod0-clk"; + compatible = "allwinner,sun5i-a13-mbus-clk"; reg = <0x01c2015c 0x4>; clocks = <&osc24M>, <&pll6 2>, <&pll5 1>; clock-output-names = "mbus"; diff --git a/arch/arm/configs/exynos_defconfig b/arch/arm/configs/exynos_defconfig index fc7d1683bf67c0b980ae6450e613e7381f4201ca..72058b8a6f4d4ccce4a8e5a740f82ba0321ef561 100644 --- a/arch/arm/configs/exynos_defconfig +++ b/arch/arm/configs/exynos_defconfig @@ -1,6 +1,7 @@ CONFIG_SYSVIPC=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y +CONFIG_CGROUPS=y CONFIG_BLK_DEV_INITRD=y CONFIG_KALLSYMS_ALL=y CONFIG_MODULES=y @@ -54,12 +55,14 @@ CONFIG_SMSC911X=y CONFIG_USB_USBNET=y CONFIG_USB_NET_SMSC75XX=y CONFIG_USB_NET_SMSC95XX=y +CONFIG_USB_GADGET=y CONFIG_INPUT_EVDEV=y CONFIG_KEYBOARD_GPIO=y CONFIG_KEYBOARD_CROS_EC=y # CONFIG_MOUSE_PS2 is not set CONFIG_MOUSE_CYAPA=y CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL_MXT=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_SAMSUNG=y CONFIG_SERIAL_SAMSUNG_CONSOLE=y @@ -68,15 +71,18 @@ CONFIG_HW_RANDOM=y CONFIG_TCG_TPM=y CONFIG_TCG_TIS_I2C_INFINEON=y CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y CONFIG_I2C_MUX=y CONFIG_I2C_ARB_GPIO_CHALLENGE=y CONFIG_I2C_EXYNOS5=y +CONFIG_I2C_GPIO=y CONFIG_I2C_CROS_EC_TUNNEL=y CONFIG_SPI=y CONFIG_SPI_S3C64XX=y CONFIG_I2C_S3C2410=y CONFIG_DEBUG_GPIO=y CONFIG_POWER_SUPPLY=y +CONFIG_BATTERY_SBS=y CONFIG_CHARGER_TPS65090=y # CONFIG_HWMON is not set CONFIG_THERMAL=y @@ -88,6 +94,7 @@ CONFIG_MFD_CROS_EC=y CONFIG_MFD_CROS_EC_I2C=y CONFIG_MFD_CROS_EC_SPI=y CONFIG_MFD_MAX77686=y +CONFIG_MFD_MAX77693=y CONFIG_MFD_MAX8997=y CONFIG_MFD_SEC_CORE=y CONFIG_MFD_TPS65090=y @@ -96,6 +103,8 @@ CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_GPIO=y CONFIG_REGULATOR_MAX8997=y CONFIG_REGULATOR_MAX77686=y +CONFIG_REGULATOR_MAX77802=y +CONFIG_REGULATOR_MAX77693=y CONFIG_REGULATOR_S2MPA01=y CONFIG_REGULATOR_S2MPS11=y CONFIG_REGULATOR_S5M8767=y @@ -115,6 +124,7 @@ CONFIG_SND_SOC=y CONFIG_SND_SOC_SAMSUNG=y CONFIG_SND_SOC_SNOW=y CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_XHCI_HCD=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_EHCI_EXYNOS=y diff --git a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig index 69c7bed3c634dd3ec2881e3b478340fbe3d4dffd..16e719c268dd77407f8ffb00fc687036d319a2b1 100644 --- a/arch/arm/configs/omap2plus_defconfig +++ b/arch/arm/configs/omap2plus_defconfig @@ -51,6 +51,7 @@ CONFIG_SOC_AM43XX=y CONFIG_SOC_DRA7XX=y CONFIG_ARM_THUMBEE=y CONFIG_ARM_ERRATA_411920=y +CONFIG_ARM_ERRATA_430973=y CONFIG_SMP=y CONFIG_NR_CPUS=2 CONFIG_CMA=y diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig index c1a4ca4f6e6d096f634da538a61380c757240076..847045313101300f9d48d5b05e587c6456d16f30 100644 --- a/arch/arm/configs/sunxi_defconfig +++ b/arch/arm/configs/sunxi_defconfig @@ -75,6 +75,7 @@ CONFIG_POWER_RESET_SUN6I=y CONFIG_WATCHDOG=y CONFIG_SUNXI_WATCHDOG=y CONFIG_MFD_AXP20X=y +CONFIG_REGULATOR=y CONFIG_REGULATOR_GPIO=y CONFIG_USB=y CONFIG_USB_EHCI_HCD=y diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h index 3f688b458143503f3f369a2c89d3f2eabd3b0934..acb0d5712716050139f83145fc75a7fe21c0b476 100644 --- a/arch/arm/include/asm/kvm_mmu.h +++ b/arch/arm/include/asm/kvm_mmu.h @@ -37,6 +37,11 @@ */ #define TRAMPOLINE_VA UL(CONFIG_VECTORS_BASE) +/* + * KVM_MMU_CACHE_MIN_PAGES is the number of stage2 page table translation levels. + */ +#define KVM_MMU_CACHE_MIN_PAGES 2 + #ifndef __ASSEMBLY__ #include @@ -50,7 +55,7 @@ void free_hyp_pgds(void); int kvm_alloc_stage2_pgd(struct kvm *kvm); void kvm_free_stage2_pgd(struct kvm *kvm); int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa, - phys_addr_t pa, unsigned long size); + phys_addr_t pa, unsigned long size, bool writable); int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run); @@ -83,6 +88,11 @@ static inline void kvm_clean_pgd(pgd_t *pgd) clean_dcache_area(pgd, PTRS_PER_S2_PGD * sizeof(pgd_t)); } +static inline void kvm_clean_pmd(pmd_t *pmd) +{ + clean_dcache_area(pmd, PTRS_PER_PMD * sizeof(pmd_t)); +} + static inline void kvm_clean_pmd_entry(pmd_t *pmd) { clean_pmd_entry(pmd); @@ -123,10 +133,23 @@ static inline bool kvm_page_empty(void *ptr) } -#define kvm_pte_table_empty(ptep) kvm_page_empty(ptep) -#define kvm_pmd_table_empty(pmdp) kvm_page_empty(pmdp) -#define kvm_pud_table_empty(pudp) (0) +#define kvm_pte_table_empty(kvm, ptep) kvm_page_empty(ptep) +#define kvm_pmd_table_empty(kvm, pmdp) kvm_page_empty(pmdp) +#define kvm_pud_table_empty(kvm, pudp) (0) + +#define KVM_PREALLOC_LEVEL 0 +static inline int kvm_prealloc_hwpgd(struct kvm *kvm, pgd_t *pgd) +{ + return 0; +} + +static inline void kvm_free_hwpgd(struct kvm *kvm) { } + +static inline void *kvm_get_hwpgd(struct kvm *kvm) +{ + return kvm->arch.pgd; +} struct kvm; diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h index 90aa4583b3086812e7bbea226f9092c0cc422cd7..3b30062975b247165dc8f8db946f304f15a9c50a 100644 --- a/arch/arm/include/asm/pgtable.h +++ b/arch/arm/include/asm/pgtable.h @@ -100,7 +100,7 @@ extern pgprot_t pgprot_s2_device; #define PAGE_HYP _MOD_PROT(pgprot_kernel, L_PTE_HYP) #define PAGE_HYP_DEVICE _MOD_PROT(pgprot_hyp_device, L_PTE_HYP) #define PAGE_S2 _MOD_PROT(pgprot_s2, L_PTE_S2_RDONLY) -#define PAGE_S2_DEVICE _MOD_PROT(pgprot_s2_device, L_PTE_S2_RDWR) +#define PAGE_S2_DEVICE _MOD_PROT(pgprot_s2_device, L_PTE_S2_RDONLY) #define __PAGE_NONE __pgprot(_L_PTE_DEFAULT | L_PTE_RDONLY | L_PTE_XN | L_PTE_NONE) #define __PAGE_SHARED __pgprot(_L_PTE_DEFAULT | L_PTE_USER | L_PTE_XN) diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 5e772a21ab9707916b82cb4d2a42d24fcb1bf878..ef9119f7462ea11550fc0e6edaa756d9edc39905 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -949,8 +949,8 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs, int scno) if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) trace_sys_enter(regs, scno); - audit_syscall_entry(AUDIT_ARCH_ARM, scno, regs->ARM_r0, regs->ARM_r1, - regs->ARM_r2, regs->ARM_r3); + audit_syscall_entry(scno, regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, + regs->ARM_r3); return scno; } diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index dfc32130bc443ddc407d463b486109ba49632db2..93090213c71ce9f860f018bd1fecd495950c2f0b 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -92,7 +92,7 @@ static int twd_timer_ack(void) static void twd_timer_stop(void) { - struct clock_event_device *clk = __this_cpu_ptr(twd_evt); + struct clock_event_device *clk = raw_cpu_ptr(twd_evt); twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk); disable_percpu_irq(clk->irq); @@ -108,7 +108,7 @@ static void twd_update_frequency(void *new_rate) { twd_timer_rate = *((unsigned long *) new_rate); - clockevents_update_freq(__this_cpu_ptr(twd_evt), twd_timer_rate); + clockevents_update_freq(raw_cpu_ptr(twd_evt), twd_timer_rate); } static int twd_rate_change(struct notifier_block *nb, @@ -134,7 +134,7 @@ static struct notifier_block twd_clk_nb = { static int twd_clk_init(void) { - if (twd_evt && __this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk)) + if (twd_evt && raw_cpu_ptr(twd_evt) && !IS_ERR(twd_clk)) return clk_notifier_register(twd_clk, &twd_clk_nb); return 0; @@ -153,7 +153,7 @@ static void twd_update_frequency(void *data) { twd_timer_rate = clk_get_rate(twd_clk); - clockevents_update_freq(__this_cpu_ptr(twd_evt), twd_timer_rate); + clockevents_update_freq(raw_cpu_ptr(twd_evt), twd_timer_rate); } static int twd_cpufreq_transition(struct notifier_block *nb, @@ -179,7 +179,7 @@ static struct notifier_block twd_cpufreq_nb = { static int twd_cpufreq_init(void) { - if (twd_evt && __this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk)) + if (twd_evt && raw_cpu_ptr(twd_evt) && !IS_ERR(twd_clk)) return cpufreq_register_notifier(&twd_cpufreq_nb, CPUFREQ_TRANSITION_NOTIFIER); @@ -269,7 +269,7 @@ static void twd_get_clock(struct device_node *np) */ static void twd_timer_setup(void) { - struct clock_event_device *clk = __this_cpu_ptr(twd_evt); + struct clock_event_device *clk = raw_cpu_ptr(twd_evt); int cpu = smp_processor_id(); /* diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index 779605122f322e4d0f342c2adeb3d4834f4261e2..9e193c8a959eaa1492838ae2913548aa7c6a80de 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -409,7 +409,7 @@ static void update_vttbr(struct kvm *kvm) kvm_next_vmid++; /* update vttbr to be used with the new vmid */ - pgd_phys = virt_to_phys(kvm->arch.pgd); + pgd_phys = virt_to_phys(kvm_get_hwpgd(kvm)); BUG_ON(pgd_phys & ~VTTBR_BADDR_MASK); vmid = ((u64)(kvm->arch.vmid) << VTTBR_VMID_SHIFT) & VTTBR_VMID_MASK; kvm->arch.vttbr = pgd_phys | vmid; @@ -808,7 +808,8 @@ static int hyp_init_cpu_notify(struct notifier_block *self, switch (action) { case CPU_STARTING: case CPU_STARTING_FROZEN: - cpu_init_hyp_mode(NULL); + if (__hyp_get_vectors() == hyp_default_vectors) + cpu_init_hyp_mode(NULL); break; } diff --git a/arch/arm/kvm/interrupts_head.S b/arch/arm/kvm/interrupts_head.S index 98c8c5b9a87f392a0410a1ca51481e93172bad8f..14d488388480ea50a80d24b18bbed9636c8d1c25 100644 --- a/arch/arm/kvm/interrupts_head.S +++ b/arch/arm/kvm/interrupts_head.S @@ -433,10 +433,17 @@ ARM_BE8(rev r10, r10 ) str r3, [r11, #VGIC_V2_CPU_HCR] str r4, [r11, #VGIC_V2_CPU_VMCR] str r5, [r11, #VGIC_V2_CPU_MISR] +#ifdef CONFIG_CPU_ENDIAN_BE8 + str r6, [r11, #(VGIC_V2_CPU_EISR + 4)] + str r7, [r11, #VGIC_V2_CPU_EISR] + str r8, [r11, #(VGIC_V2_CPU_ELRSR + 4)] + str r9, [r11, #VGIC_V2_CPU_ELRSR] +#else str r6, [r11, #VGIC_V2_CPU_EISR] str r7, [r11, #(VGIC_V2_CPU_EISR + 4)] str r8, [r11, #VGIC_V2_CPU_ELRSR] str r9, [r11, #(VGIC_V2_CPU_ELRSR + 4)] +#endif str r10, [r11, #VGIC_V2_CPU_APR] /* Clear GICH_HCR */ diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c index eea03069161b4b4c2824bd0908edac065f0959a8..57a403a5c22bf9e174ec88a0377b4ab07c3b0a29 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c @@ -42,7 +42,7 @@ static unsigned long hyp_idmap_start; static unsigned long hyp_idmap_end; static phys_addr_t hyp_idmap_vector; -#define pgd_order get_order(PTRS_PER_PGD * sizeof(pgd_t)) +#define hyp_pgd_order get_order(PTRS_PER_PGD * sizeof(pgd_t)) #define kvm_pmd_huge(_x) (pmd_huge(_x) || pmd_trans_huge(_x)) @@ -134,7 +134,7 @@ static void unmap_ptes(struct kvm *kvm, pmd_t *pmd, } } while (pte++, addr += PAGE_SIZE, addr != end); - if (kvm_pte_table_empty(start_pte)) + if (kvm_pte_table_empty(kvm, start_pte)) clear_pmd_entry(kvm, pmd, start_addr); } @@ -158,7 +158,7 @@ static void unmap_pmds(struct kvm *kvm, pud_t *pud, } } while (pmd++, addr = next, addr != end); - if (kvm_pmd_table_empty(start_pmd)) + if (kvm_pmd_table_empty(kvm, start_pmd)) clear_pud_entry(kvm, pud, start_addr); } @@ -182,7 +182,7 @@ static void unmap_puds(struct kvm *kvm, pgd_t *pgd, } } while (pud++, addr = next, addr != end); - if (kvm_pud_table_empty(start_pud)) + if (kvm_pud_table_empty(kvm, start_pud)) clear_pgd_entry(kvm, pgd, start_addr); } @@ -306,7 +306,7 @@ void free_boot_hyp_pgd(void) if (boot_hyp_pgd) { unmap_range(NULL, boot_hyp_pgd, hyp_idmap_start, PAGE_SIZE); unmap_range(NULL, boot_hyp_pgd, TRAMPOLINE_VA, PAGE_SIZE); - free_pages((unsigned long)boot_hyp_pgd, pgd_order); + free_pages((unsigned long)boot_hyp_pgd, hyp_pgd_order); boot_hyp_pgd = NULL; } @@ -343,7 +343,7 @@ void free_hyp_pgds(void) for (addr = VMALLOC_START; is_vmalloc_addr((void*)addr); addr += PGDIR_SIZE) unmap_range(NULL, hyp_pgd, KERN_TO_HYP(addr), PGDIR_SIZE); - free_pages((unsigned long)hyp_pgd, pgd_order); + free_pages((unsigned long)hyp_pgd, hyp_pgd_order); hyp_pgd = NULL; } @@ -401,13 +401,46 @@ static int create_hyp_pmd_mappings(pud_t *pud, unsigned long start, return 0; } +static int create_hyp_pud_mappings(pgd_t *pgd, unsigned long start, + unsigned long end, unsigned long pfn, + pgprot_t prot) +{ + pud_t *pud; + pmd_t *pmd; + unsigned long addr, next; + int ret; + + addr = start; + do { + pud = pud_offset(pgd, addr); + + if (pud_none_or_clear_bad(pud)) { + pmd = pmd_alloc_one(NULL, addr); + if (!pmd) { + kvm_err("Cannot allocate Hyp pmd\n"); + return -ENOMEM; + } + pud_populate(NULL, pud, pmd); + get_page(virt_to_page(pud)); + kvm_flush_dcache_to_poc(pud, sizeof(*pud)); + } + + next = pud_addr_end(addr, end); + ret = create_hyp_pmd_mappings(pud, addr, next, pfn, prot); + if (ret) + return ret; + pfn += (next - addr) >> PAGE_SHIFT; + } while (addr = next, addr != end); + + return 0; +} + static int __create_hyp_mappings(pgd_t *pgdp, unsigned long start, unsigned long end, unsigned long pfn, pgprot_t prot) { pgd_t *pgd; pud_t *pud; - pmd_t *pmd; unsigned long addr, next; int err = 0; @@ -416,22 +449,21 @@ static int __create_hyp_mappings(pgd_t *pgdp, end = PAGE_ALIGN(end); do { pgd = pgdp + pgd_index(addr); - pud = pud_offset(pgd, addr); - if (pud_none_or_clear_bad(pud)) { - pmd = pmd_alloc_one(NULL, addr); - if (!pmd) { - kvm_err("Cannot allocate Hyp pmd\n"); + if (pgd_none(*pgd)) { + pud = pud_alloc_one(NULL, addr); + if (!pud) { + kvm_err("Cannot allocate Hyp pud\n"); err = -ENOMEM; goto out; } - pud_populate(NULL, pud, pmd); - get_page(virt_to_page(pud)); - kvm_flush_dcache_to_poc(pud, sizeof(*pud)); + pgd_populate(NULL, pgd, pud); + get_page(virt_to_page(pgd)); + kvm_flush_dcache_to_poc(pgd, sizeof(*pgd)); } next = pgd_addr_end(addr, end); - err = create_hyp_pmd_mappings(pud, addr, next, pfn, prot); + err = create_hyp_pud_mappings(pgd, addr, next, pfn, prot); if (err) goto out; pfn += (next - addr) >> PAGE_SHIFT; @@ -521,6 +553,7 @@ int create_hyp_io_mappings(void *from, void *to, phys_addr_t phys_addr) */ int kvm_alloc_stage2_pgd(struct kvm *kvm) { + int ret; pgd_t *pgd; if (kvm->arch.pgd != NULL) { @@ -528,15 +561,38 @@ int kvm_alloc_stage2_pgd(struct kvm *kvm) return -EINVAL; } - pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, S2_PGD_ORDER); + if (KVM_PREALLOC_LEVEL > 0) { + /* + * Allocate fake pgd for the page table manipulation macros to + * work. This is not used by the hardware and we have no + * alignment requirement for this allocation. + */ + pgd = (pgd_t *)kmalloc(PTRS_PER_S2_PGD * sizeof(pgd_t), + GFP_KERNEL | __GFP_ZERO); + } else { + /* + * Allocate actual first-level Stage-2 page table used by the + * hardware for Stage-2 page table walks. + */ + pgd = (pgd_t *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, S2_PGD_ORDER); + } + if (!pgd) return -ENOMEM; - memset(pgd, 0, PTRS_PER_S2_PGD * sizeof(pgd_t)); + ret = kvm_prealloc_hwpgd(kvm, pgd); + if (ret) + goto out_err; + kvm_clean_pgd(pgd); kvm->arch.pgd = pgd; - return 0; +out_err: + if (KVM_PREALLOC_LEVEL > 0) + kfree(pgd); + else + free_pages((unsigned long)pgd, S2_PGD_ORDER); + return ret; } /** @@ -572,19 +628,39 @@ void kvm_free_stage2_pgd(struct kvm *kvm) return; unmap_stage2_range(kvm, 0, KVM_PHYS_SIZE); - free_pages((unsigned long)kvm->arch.pgd, S2_PGD_ORDER); + kvm_free_hwpgd(kvm); + if (KVM_PREALLOC_LEVEL > 0) + kfree(kvm->arch.pgd); + else + free_pages((unsigned long)kvm->arch.pgd, S2_PGD_ORDER); kvm->arch.pgd = NULL; } -static pmd_t *stage2_get_pmd(struct kvm *kvm, struct kvm_mmu_memory_cache *cache, +static pud_t *stage2_get_pud(struct kvm *kvm, struct kvm_mmu_memory_cache *cache, phys_addr_t addr) { pgd_t *pgd; pud_t *pud; - pmd_t *pmd; pgd = kvm->arch.pgd + pgd_index(addr); - pud = pud_offset(pgd, addr); + if (WARN_ON(pgd_none(*pgd))) { + if (!cache) + return NULL; + pud = mmu_memory_cache_alloc(cache); + pgd_populate(NULL, pgd, pud); + get_page(virt_to_page(pgd)); + } + + return pud_offset(pgd, addr); +} + +static pmd_t *stage2_get_pmd(struct kvm *kvm, struct kvm_mmu_memory_cache *cache, + phys_addr_t addr) +{ + pud_t *pud; + pmd_t *pmd; + + pud = stage2_get_pud(kvm, cache, addr); if (pud_none(*pud)) { if (!cache) return NULL; @@ -630,7 +706,7 @@ static int stage2_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache, pmd_t *pmd; pte_t *pte, old_pte; - /* Create stage-2 page table mapping - Level 1 */ + /* Create stage-2 page table mapping - Levels 0 and 1 */ pmd = stage2_get_pmd(kvm, cache, addr); if (!pmd) { /* @@ -675,7 +751,7 @@ static int stage2_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache, * @size: The size of the mapping */ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa, - phys_addr_t pa, unsigned long size) + phys_addr_t pa, unsigned long size, bool writable) { phys_addr_t addr, end; int ret = 0; @@ -688,7 +764,11 @@ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa, for (addr = guest_ipa; addr < end; addr += PAGE_SIZE) { pte_t pte = pfn_pte(pfn, PAGE_S2_DEVICE); - ret = mmu_topup_memory_cache(&cache, 2, 2); + if (writable) + kvm_set_s2pte_writable(&pte); + + ret = mmu_topup_memory_cache(&cache, KVM_MMU_CACHE_MIN_PAGES, + KVM_NR_MEM_OBJS); if (ret) goto out; spin_lock(&kvm->mmu_lock); @@ -777,6 +857,12 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, /* Let's check if we will get back a huge page backed by hugetlbfs */ down_read(¤t->mm->mmap_sem); vma = find_vma_intersection(current->mm, hva, hva + 1); + if (unlikely(!vma)) { + kvm_err("Failed to find VMA for hva 0x%lx\n", hva); + up_read(¤t->mm->mmap_sem); + return -EFAULT; + } + if (is_vm_hugetlb_page(vma)) { hugetlb = true; gfn = (fault_ipa & PMD_MASK) >> PAGE_SHIFT; @@ -797,7 +883,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, up_read(¤t->mm->mmap_sem); /* We need minimum second+third level pages */ - ret = mmu_topup_memory_cache(memcache, 2, KVM_NR_MEM_OBJS); + ret = mmu_topup_memory_cache(memcache, KVM_MMU_CACHE_MIN_PAGES, + KVM_NR_MEM_OBJS); if (ret) return ret; @@ -843,7 +930,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, } coherent_cache_guest_page(vcpu, hva, PAGE_SIZE); ret = stage2_set_pte(kvm, memcache, fault_ipa, &new_pte, - mem_type == PAGE_S2_DEVICE); + pgprot_val(mem_type) == pgprot_val(PAGE_S2_DEVICE)); } @@ -916,6 +1003,9 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run) goto out_unlock; } + /* Userspace should not be able to register out-of-bounds IPAs */ + VM_BUG_ON(fault_ipa >= KVM_PHYS_SIZE); + ret = user_mem_abort(vcpu, fault_ipa, memslot, hva, fault_status); if (ret == 0) ret = 1; @@ -1072,8 +1162,8 @@ int kvm_mmu_init(void) (unsigned long)phys_base); } - hyp_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, pgd_order); - boot_hyp_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, pgd_order); + hyp_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, hyp_pgd_order); + boot_hyp_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, hyp_pgd_order); if (!hyp_pgd || !boot_hyp_pgd) { kvm_err("Hyp mode PGD not allocated\n"); @@ -1126,13 +1216,6 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, const struct kvm_memory_slot *old, enum kvm_mr_change change) { - gpa_t gpa = old->base_gfn << PAGE_SHIFT; - phys_addr_t size = old->npages << PAGE_SHIFT; - if (change == KVM_MR_DELETE || change == KVM_MR_MOVE) { - spin_lock(&kvm->mmu_lock); - unmap_stage2_range(kvm, gpa, size); - spin_unlock(&kvm->mmu_lock); - } } int kvm_arch_prepare_memory_region(struct kvm *kvm, @@ -1140,7 +1223,77 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, enum kvm_mr_change change) { - return 0; + hva_t hva = mem->userspace_addr; + hva_t reg_end = hva + mem->memory_size; + bool writable = !(mem->flags & KVM_MEM_READONLY); + int ret = 0; + + if (change != KVM_MR_CREATE && change != KVM_MR_MOVE) + return 0; + + /* + * Prevent userspace from creating a memory region outside of the IPA + * space addressable by the KVM guest IPA space. + */ + if (memslot->base_gfn + memslot->npages >= + (KVM_PHYS_SIZE >> PAGE_SHIFT)) + return -EFAULT; + + /* + * A memory region could potentially cover multiple VMAs, and any holes + * between them, so iterate over all of them to find out if we can map + * any of them right now. + * + * +--------------------------------------------+ + * +---------------+----------------+ +----------------+ + * | : VMA 1 | VMA 2 | | VMA 3 : | + * +---------------+----------------+ +----------------+ + * | memory region | + * +--------------------------------------------+ + */ + do { + struct vm_area_struct *vma = find_vma(current->mm, hva); + hva_t vm_start, vm_end; + + if (!vma || vma->vm_start >= reg_end) + break; + + /* + * Mapping a read-only VMA is only allowed if the + * memory region is configured as read-only. + */ + if (writable && !(vma->vm_flags & VM_WRITE)) { + ret = -EPERM; + break; + } + + /* + * Take the intersection of this VMA with the memory region + */ + vm_start = max(hva, vma->vm_start); + vm_end = min(reg_end, vma->vm_end); + + if (vma->vm_flags & VM_PFNMAP) { + gpa_t gpa = mem->guest_phys_addr + + (vm_start - mem->userspace_addr); + phys_addr_t pa = (vma->vm_pgoff << PAGE_SHIFT) + + vm_start - vma->vm_start; + + ret = kvm_phys_addr_ioremap(kvm, gpa, pa, + vm_end - vm_start, + writable); + if (ret) + break; + } + hva = vm_end; + } while (hva < reg_end); + + if (ret) { + spin_lock(&kvm->mmu_lock); + unmap_stage2_range(kvm, mem->guest_phys_addr, mem->memory_size); + spin_unlock(&kvm->mmu_lock); + } + return ret; } void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free, @@ -1165,4 +1318,10 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm) void kvm_arch_flush_shadow_memslot(struct kvm *kvm, struct kvm_memory_slot *slot) { + gpa_t gpa = slot->base_gfn << PAGE_SHIFT; + phys_addr_t size = slot->npages << PAGE_SHIFT; + + spin_lock(&kvm->mmu_lock); + unmap_stage2_range(kvm, gpa, size); + spin_unlock(&kvm->mmu_lock); } diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile index ac99d87ffefea5562346f454ae145c1f3e94200e..1b9ae0257a6eac6321c94bea9ccde88a286731fd 100644 --- a/arch/arm/mach-at91/Makefile +++ b/arch/arm/mach-at91/Makefile @@ -3,9 +3,6 @@ # obj-y := gpio.o setup.o sysirq_mask.o -obj-m := -obj-n := -obj- := obj-$(CONFIG_OLD_IRQ_AT91) += irq.o obj-$(CONFIG_OLD_CLK_AT91) += clock.o diff --git a/arch/arm/mach-ebsa110/Makefile b/arch/arm/mach-ebsa110/Makefile index 935e4af01a2769a7ab83282bda801389f36a4d50..a7d68c13c1d16fb53ef40d4f3633915ba3b2448f 100644 --- a/arch/arm/mach-ebsa110/Makefile +++ b/arch/arm/mach-ebsa110/Makefile @@ -5,6 +5,3 @@ # Object file lists. obj-y := core.o io.o leds.o -obj-m := -obj-n := -obj- := diff --git a/arch/arm/mach-ep93xx/Makefile b/arch/arm/mach-ep93xx/Makefile index 0dc51f9462ded5223839c8172d887da5b01fb063..78d427b34b1f2953c391af495d8e4f479ba6e811 100644 --- a/arch/arm/mach-ep93xx/Makefile +++ b/arch/arm/mach-ep93xx/Makefile @@ -2,9 +2,6 @@ # Makefile for the linux kernel. # obj-y := core.o clock.o -obj-m := -obj-n := -obj- := obj-$(CONFIG_EP93XX_DMA) += dma.o diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index 788f26d21141df6c67c0511e51d9a72d4b02c86e..27ae6144679c6a369087420836bf7210b728f116 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -7,11 +7,6 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) += -I$(srctree)/$(src)/include -I$(srctree)/arch/arm/plat-samsung/include -obj-y := -obj-m := -obj-n := -obj- := - # Core obj-$(CONFIG_ARCH_EXYNOS) += exynos.o pmu.o exynos-smc.o firmware.o diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index b89e5f35db841e50b64a5790af79e0c11c1e783f..6b283eb3202ec2ecac7e7742e82ab284f38c808c 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c @@ -161,7 +161,9 @@ static void exynos_restart(enum reboot_mode mode, const char *cmd) static struct platform_device exynos_cpuidle = { .name = "exynos_cpuidle", +#ifdef CONFIG_ARM_EXYNOS_CPUIDLE .dev.platform_data = exynos_enter_aftr, +#endif .id = -1, }; diff --git a/arch/arm/mach-footbridge/Makefile b/arch/arm/mach-footbridge/Makefile index c3faa3bc84dd3ea49321d06ae997132dabd12c7b..e83d5c8396ff720a9c1cb8a3e9095fe4d9166f3c 100644 --- a/arch/arm/mach-footbridge/Makefile +++ b/arch/arm/mach-footbridge/Makefile @@ -5,9 +5,6 @@ # Object file lists. obj-y := common.o dma.o isa-irq.o -obj-m := -obj-n := -obj- := pci-y += dc21285.o pci-$(CONFIG_ARCH_CATS) += cats-pci.o diff --git a/arch/arm/mach-iop13xx/Makefile b/arch/arm/mach-iop13xx/Makefile index cad015fee12febbefa2e43fb5dbaaa3d39cfafcc..a3d9260e335f2f1e5f5e24119fc2e3cb247f00bd 100644 --- a/arch/arm/mach-iop13xx/Makefile +++ b/arch/arm/mach-iop13xx/Makefile @@ -1,8 +1,3 @@ -obj-y := -obj-m := -obj-n := -obj- := - obj-$(CONFIG_ARCH_IOP13XX) += setup.o obj-$(CONFIG_ARCH_IOP13XX) += irq.o obj-$(CONFIG_ARCH_IOP13XX) += pci.o diff --git a/arch/arm/mach-iop32x/Makefile b/arch/arm/mach-iop32x/Makefile index cfdf8a137c2b9f8911771d87859b0edb4934019b..2d4010abb82fb7e9bcd4e00199f14766d67a353b 100644 --- a/arch/arm/mach-iop32x/Makefile +++ b/arch/arm/mach-iop32x/Makefile @@ -3,9 +3,6 @@ # obj-y := irq.o -obj-m := -obj-n := -obj- := obj-$(CONFIG_MACH_GLANTANK) += glantank.o obj-$(CONFIG_ARCH_IQ80321) += iq80321.o diff --git a/arch/arm/mach-iop33x/Makefile b/arch/arm/mach-iop33x/Makefile index 90081d8c9d16c862da18602d851d7aa92cbea5e5..e95db30d81d5ba74d34551a86020c66a0a40c431 100644 --- a/arch/arm/mach-iop33x/Makefile +++ b/arch/arm/mach-iop33x/Makefile @@ -3,9 +3,6 @@ # obj-y := irq.o uart.o -obj-m := -obj-n := -obj- := obj-$(CONFIG_ARCH_IQ80331) += iq80331.o obj-$(CONFIG_MACH_IQ80332) += iq80332.o diff --git a/arch/arm/mach-ks8695/Makefile b/arch/arm/mach-ks8695/Makefile index e370caf0c91beacf86ba38305e97f7182f3e4730..8ecb7973ae542c4fa8f89a8f29e9f1ccb6d95c72 100644 --- a/arch/arm/mach-ks8695/Makefile +++ b/arch/arm/mach-ks8695/Makefile @@ -4,9 +4,6 @@ # obj-y := cpu.o irq.o time.o devices.o -obj-m := -obj-n := -obj- := # PCI support is optional obj-$(CONFIG_PCI) += pci.o diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index a4d52c42a438e15d3b60b8b3494690fa955f7403..5fa3755261ce661fe11c6b8d9b0094d14fb317dc 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -1440,6 +1440,8 @@ static int gpmc_probe_nand_child(struct platform_device *pdev, break; } + gpmc_nand_data->flash_bbt = of_get_nand_on_flash_bbt(child); + val = of_get_nand_bus_width(child); if (val == 16) gpmc_nand_data->devsize = NAND_BUSWIDTH_16; diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c index b8ad045bcb8dfbe528a40b93657e0bf373e1832d..03cbb16898a3f0eeef87144ea70f5b8d96849650 100644 --- a/arch/arm/mach-omap2/io.c +++ b/arch/arm/mach-omap2/io.c @@ -723,8 +723,16 @@ int __init omap_clk_init(void) ti_clk_init_features(); ret = of_prcm_init(); - if (!ret) - ret = omap_clk_soc_init(); + if (ret) + return ret; + + of_clk_init(NULL); + + ti_dt_clk_init_retry_clks(); + + ti_dt_clockdomains_setup(); + + ret = omap_clk_soc_init(); return ret; } diff --git a/arch/arm/mach-omap2/prm_common.c b/arch/arm/mach-omap2/prm_common.c index 74054b81360079aa835a80a68b0ddc453b393829..ee2b5222eac07f279e87a6126d7ed5a864d12d73 100644 --- a/arch/arm/mach-omap2/prm_common.c +++ b/arch/arm/mach-omap2/prm_common.c @@ -525,8 +525,6 @@ int __init of_prcm_init(void) memmap_index++; } - ti_dt_clockdomains_setup(); - return 0; } diff --git a/arch/arm/mach-pxa/include/mach/pxa2xx-regs.h b/arch/arm/mach-pxa/include/mach/pxa2xx-regs.h index ee6ced1cea7f1705cda89be3bff0b3d51356e4cf..f1dd62946b3699f6f25b7dd2683d98de9df141a1 100644 --- a/arch/arm/mach-pxa/include/mach/pxa2xx-regs.h +++ b/arch/arm/mach-pxa/include/mach/pxa2xx-regs.h @@ -143,6 +143,16 @@ #define CCCR_M_MASK 0x0060 /* Memory Frequency to Run Mode Frequency Multiplier */ #define CCCR_L_MASK 0x001f /* Crystal Frequency to Memory Frequency Multiplier */ +#define CCCR_CPDIS_BIT (31) +#define CCCR_PPDIS_BIT (30) +#define CCCR_LCD_26_BIT (27) +#define CCCR_A_BIT (25) + +#define CCSR_N2_MASK CCCR_N_MASK +#define CCSR_M_MASK CCCR_M_MASK +#define CCSR_L_MASK CCCR_L_MASK +#define CCSR_N2_SHIFT 7 + #define CKEN_AC97CONF (31) /* AC97 Controller Configuration */ #define CKEN_CAMERA (24) /* Camera Interface Clock Enable */ #define CKEN_SSP1 (23) /* SSP1 Unit Clock Enable */ diff --git a/arch/arm/mach-pxa/lpd270.c b/arch/arm/mach-pxa/lpd270.c index 9f6ec167902a6bc1c2db823d257e006550a3c3b0..ad777b353bd5234797d93031ab6815747b65e363 100644 --- a/arch/arm/mach-pxa/lpd270.c +++ b/arch/arm/mach-pxa/lpd270.c @@ -416,17 +416,17 @@ static struct pxafb_mach_info *lpd270_lcd_to_use; static int __init lpd270_set_lcd(char *str) { - if (!strnicmp(str, "lq057q3dc02", 11)) { + if (!strncasecmp(str, "lq057q3dc02", 11)) { lpd270_lcd_to_use = &sharp_lq057q3dc02; - } else if (!strnicmp(str, "lq121s1dg31", 11)) { + } else if (!strncasecmp(str, "lq121s1dg31", 11)) { lpd270_lcd_to_use = &sharp_lq121s1dg31; - } else if (!strnicmp(str, "lq036q1da01", 11)) { + } else if (!strncasecmp(str, "lq036q1da01", 11)) { lpd270_lcd_to_use = &sharp_lq036q1da01; - } else if (!strnicmp(str, "lq64d343", 8)) { + } else if (!strncasecmp(str, "lq64d343", 8)) { lpd270_lcd_to_use = &sharp_lq64d343; - } else if (!strnicmp(str, "lq10d368", 8)) { + } else if (!strncasecmp(str, "lq10d368", 8)) { lpd270_lcd_to_use = &sharp_lq10d368; - } else if (!strnicmp(str, "lq035q7db02-20", 14)) { + } else if (!strncasecmp(str, "lq035q7db02-20", 14)) { lpd270_lcd_to_use = &sharp_lq035q7db02_20; } else { printk(KERN_INFO "lpd270: unknown lcd panel [%s]\n", str); diff --git a/arch/arm/mach-rpc/Makefile b/arch/arm/mach-rpc/Makefile index 992e28b4ae9a54ddab9ea92b2d0c92d6b303964e..2ebc6875aeb8f69dc4e27fd1bc3f636e8b2125f2 100644 --- a/arch/arm/mach-rpc/Makefile +++ b/arch/arm/mach-rpc/Makefile @@ -5,7 +5,3 @@ # Object file lists. obj-y := dma.o ecard.o fiq.o irq.o riscpc.o time.o -obj-m := -obj-n := -obj- := - diff --git a/arch/arm/mach-s3c24xx/Makefile b/arch/arm/mach-s3c24xx/Makefile index 2235d0d3b38dd933b81687bcc681001a2a44d8d8..b92071638733badcd7f69d65c685e35d731773a8 100644 --- a/arch/arm/mach-s3c24xx/Makefile +++ b/arch/arm/mach-s3c24xx/Makefile @@ -7,11 +7,6 @@ # # Licensed under GPLv2 -obj-y := -obj-m := -obj-n := -obj- := - # core obj-y += common.o diff --git a/arch/arm/mach-s3c64xx/Makefile b/arch/arm/mach-s3c64xx/Makefile index 58069a702a435046ae627bff9ab4e8c689b1aa3c..12f67b61ca5f7e7340821f21744530bef0ec844d 100644 --- a/arch/arm/mach-s3c64xx/Makefile +++ b/arch/arm/mach-s3c64xx/Makefile @@ -5,11 +5,6 @@ # # Licensed under GPLv2 -obj-y := -obj-m := -obj-n := -obj- := - # Core obj-y += common.o diff --git a/arch/arm/mach-s5pv210/Makefile b/arch/arm/mach-s5pv210/Makefile index 7dc2d0e25a83363e337e5e6e8433ee66ae1527cc..72b9e96715070f2c47b13b7f7aade101abb9586e 100644 --- a/arch/arm/mach-s5pv210/Makefile +++ b/arch/arm/mach-s5pv210/Makefile @@ -7,11 +7,6 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) += -I$(srctree)/$(src)/include -I$(srctree)/arch/arm/plat-samsung/include -obj-y := -obj-m := -obj-n := -obj- := - # Core obj-$(CONFIG_PM_SLEEP) += pm.o sleep.o diff --git a/arch/arm/mach-sa1100/Makefile b/arch/arm/mach-sa1100/Makefile index 2732eef48966bfa337d34f32fb806f4e604d515c..f1114d11fe13eb21dfe51af7af00f0fb0c0e242a 100644 --- a/arch/arm/mach-sa1100/Makefile +++ b/arch/arm/mach-sa1100/Makefile @@ -4,9 +4,6 @@ # Common support obj-y := clock.o generic.o irq.o time.o #nmi-oopser.o -obj-m := -obj-n := -obj- := # Specific board support obj-$(CONFIG_SA1100_ASSABET) += assabet.o diff --git a/arch/arm/mach-shmobile/board-koelsch-reference.c b/arch/arm/mach-shmobile/board-koelsch-reference.c index 9db5e6774fb75753d3a8199301d75887132f5640..46aa540133d6b81cb05053ffaab7a1a28aa21553 100644 --- a/arch/arm/mach-shmobile/board-koelsch-reference.c +++ b/arch/arm/mach-shmobile/board-koelsch-reference.c @@ -41,16 +41,15 @@ static struct rcar_du_encoder_data koelsch_du_encoders[] = { .width_mm = 210, .height_mm = 158, .mode = { - .clock = 65000, - .hdisplay = 1024, - .hsync_start = 1048, - .hsync_end = 1184, - .htotal = 1344, - .vdisplay = 768, - .vsync_start = 771, - .vsync_end = 777, - .vtotal = 806, - .flags = 0, + .pixelclock = 65000000, + .hactive = 1024, + .hfront_porch = 20, + .hback_porch = 160, + .hsync_len = 136, + .vactive = 768, + .vfront_porch = 3, + .vback_porch = 29, + .vsync_len = 6, }, }, }, diff --git a/arch/arm/mach-shmobile/board-koelsch.c b/arch/arm/mach-shmobile/board-koelsch.c index 126a8b4ec491fa0b6c449eb0628ad02e33ad7c1d..7111b5c1d67b764f79daec420390fea7f408c92c 100644 --- a/arch/arm/mach-shmobile/board-koelsch.c +++ b/arch/arm/mach-shmobile/board-koelsch.c @@ -63,16 +63,15 @@ static struct rcar_du_encoder_data koelsch_du_encoders[] = { .width_mm = 210, .height_mm = 158, .mode = { - .clock = 65000, - .hdisplay = 1024, - .hsync_start = 1048, - .hsync_end = 1184, - .htotal = 1344, - .vdisplay = 768, - .vsync_start = 771, - .vsync_end = 777, - .vtotal = 806, - .flags = 0, + .pixelclock = 65000000, + .hactive = 1024, + .hfront_porch = 20, + .hback_porch = 160, + .hsync_len = 136, + .vactive = 768, + .vfront_porch = 3, + .vback_porch = 29, + .vsync_len = 6, }, }, }, diff --git a/arch/arm/mach-shmobile/board-lager-reference.c b/arch/arm/mach-shmobile/board-lager-reference.c index 2a05c02bec3965f4f1dc4493b5d7fdaea40af275..bc4b48357ddea891ccb0365ba267325adff374d8 100644 --- a/arch/arm/mach-shmobile/board-lager-reference.c +++ b/arch/arm/mach-shmobile/board-lager-reference.c @@ -43,16 +43,15 @@ static struct rcar_du_encoder_data lager_du_encoders[] = { .width_mm = 210, .height_mm = 158, .mode = { - .clock = 65000, - .hdisplay = 1024, - .hsync_start = 1048, - .hsync_end = 1184, - .htotal = 1344, - .vdisplay = 768, - .vsync_start = 771, - .vsync_end = 777, - .vtotal = 806, - .flags = 0, + .pixelclock = 65000000, + .hactive = 1024, + .hfront_porch = 20, + .hback_porch = 160, + .hsync_len = 136, + .vactive = 768, + .vfront_porch = 3, + .vback_porch = 29, + .vsync_len = 6, }, }, }, diff --git a/arch/arm/mach-shmobile/board-lager.c b/arch/arm/mach-shmobile/board-lager.c index f5a98e2942b30d2e6b39f2fb00a17e3a01999a30..571327b1c942c138fbba5cfe8e5ea03d7896ccb1 100644 --- a/arch/arm/mach-shmobile/board-lager.c +++ b/arch/arm/mach-shmobile/board-lager.c @@ -99,16 +99,15 @@ static struct rcar_du_encoder_data lager_du_encoders[] = { .width_mm = 210, .height_mm = 158, .mode = { - .clock = 65000, - .hdisplay = 1024, - .hsync_start = 1048, - .hsync_end = 1184, - .htotal = 1344, - .vdisplay = 768, - .vsync_start = 771, - .vsync_end = 777, - .vtotal = 806, - .flags = 0, + .pixelclock = 65000000, + .hactive = 1024, + .hfront_porch = 20, + .hback_porch = 160, + .hsync_len = 136, + .vactive = 768, + .vfront_porch = 3, + .vback_porch = 29, + .vsync_len = 6, }, }, }, diff --git a/arch/arm/mach-shmobile/board-marzen.c b/arch/arm/mach-shmobile/board-marzen.c index e5cf4201e769645829a6c78b1c28b35b55a65085..ce33d7825c49a818a2bb1927299be684a7196544 100644 --- a/arch/arm/mach-shmobile/board-marzen.c +++ b/arch/arm/mach-shmobile/board-marzen.c @@ -192,16 +192,15 @@ static struct rcar_du_encoder_data du_encoders[] = { .width_mm = 210, .height_mm = 158, .mode = { - .clock = 65000, - .hdisplay = 1024, - .hsync_start = 1048, - .hsync_end = 1184, - .htotal = 1344, - .vdisplay = 768, - .vsync_start = 771, - .vsync_end = 777, - .vtotal = 806, - .flags = 0, + .pixelclock = 65000000, + .hactive = 1024, + .hfront_porch = 20, + .hback_porch = 160, + .hsync_len = 136, + .vactive = 768, + .vfront_porch = 3, + .vback_porch = 29, + .vsync_len = 6, }, }, }, diff --git a/arch/arm/mach-u300/Makefile b/arch/arm/mach-u300/Makefile index 3ec74ac95bc1c1087c04776c0c1b2e6176273f4f..87d37de054b65b5d273dddd29d97ac5fc0912c8b 100644 --- a/arch/arm/mach-u300/Makefile +++ b/arch/arm/mach-u300/Makefile @@ -3,9 +3,6 @@ # obj-y := core.o -obj-m := -obj-n := -obj- := obj-$(CONFIG_MACH_U300_SPIDUMMY) += dummyspichip.o obj-$(CONFIG_REGULATOR_AB3100) += regulator.o diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S index ee1d80593958715340ce2743b3d0b2370c227848..ba1196c968d82f81b2b01f775b638483bdc53401 100644 --- a/arch/arm/mm/proc-macros.S +++ b/arch/arm/mm/proc-macros.S @@ -279,7 +279,7 @@ ENTRY(\name\()_processor_functions) .if \suspend .word cpu_\name\()_suspend_size -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_ARM_CPU_SUSPEND .word cpu_\name\()_do_suspend .word cpu_\name\()_do_resume #else diff --git a/arch/arm/plat-iop/Makefile b/arch/arm/plat-iop/Makefile index 224e56c6049b04f35579e385fd0a7bed83899b1c..f2af203d601f2bb057921e3277f489797dc77fc4 100644 --- a/arch/arm/plat-iop/Makefile +++ b/arch/arm/plat-iop/Makefile @@ -2,8 +2,6 @@ # Makefile for the linux kernel. # -obj-y := - # IOP32X obj-$(CONFIG_ARCH_IOP32X) += i2c.o obj-$(CONFIG_ARCH_IOP32X) += pci.o @@ -27,7 +25,3 @@ obj-$(CONFIG_ARCH_IOP33X) += restart.o # IOP13XX obj-$(CONFIG_ARCH_IOP13XX) += cp6.o obj-$(CONFIG_ARCH_IOP13XX) += time.o - -obj-m := -obj-n := -obj- := diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index 0b01b68fd033072c818de997bd07536feb2ca64b..97a50e8883f949726912263af5075db15c4d61e8 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile @@ -6,9 +6,6 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/arch/arm/plat-omap/include # Common support obj-y := sram.o dma.o counter_32k.o -obj-m := -obj-n := -obj- := # omap_device support (OMAP2+ only at the moment) diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index c87aefbf3a13e80467fc6b9326eeceed24f2e5c3..9bd2776e7d05fba57e937f3b7cd469f80cd741ff 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -268,8 +268,9 @@ config S5P_DEV_MFC comment "Power management" config SAMSUNG_PM_DEBUG - bool "S3C2410 PM Suspend debug" - depends on PM && DEBUG_KERNEL && DEBUG_S3C_UART + bool "Samsung PM Suspend debug" + depends on PM && DEBUG_KERNEL + depends on DEBUG_EXYNOS_UART || DEBUG_S3C24XX_UART || DEBUG_S3C2410_UART help Say Y here if you want verbose debugging from the PM Suspend and Resume code. See diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index 5fe175017f07bad4d698ad226e38b2c41cec0bb4..f0a008496993ec1a5fc1625068ffb69aa510a3f1 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -6,11 +6,6 @@ ccflags-$(CONFIG_ARCH_MULTI_V7) += -I$(srctree)/$(src)/include -obj-y := -obj-m := -obj-n := dummy.o -obj- := - # Objects we always build independent of SoC choice obj-y += init.o cpu.o diff --git a/arch/arm/plat-samsung/pm-debug.c b/arch/arm/plat-samsung/pm-debug.c index 8f19f66388ddfa850f4f86f34a188bba2ce5ef4d..39609601f407fdc1b0f5891e23646ce45207fc6f 100644 --- a/arch/arm/plat-samsung/pm-debug.c +++ b/arch/arm/plat-samsung/pm-debug.c @@ -14,6 +14,7 @@ */ #include +#include #include #include diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 59c86b6b30520444d77f061d677049acb8e694b7..20901ffed182a6d1b1f41c08f91109e1ac2047ef 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -30,8 +30,6 @@ AS += -EL LD += -EL endif -comma = , - CHECKFLAGS += -D__aarch64__ # Default value diff --git a/arch/arm64/boot/dts/apm-mustang.dts b/arch/arm64/boot/dts/apm-mustang.dts index 8eb6d94c785154cb3c29dba770cde5fed6eaec50..2e25de0800b9f061bebb4ee81c4c2ffaa1cf0dc9 100644 --- a/arch/arm64/boot/dts/apm-mustang.dts +++ b/arch/arm64/boot/dts/apm-mustang.dts @@ -41,6 +41,10 @@ status = "ok"; }; +&sgenet0 { + status = "ok"; +}; + &xgenet { status = "ok"; }; diff --git a/arch/arm64/boot/dts/apm-storm.dtsi b/arch/arm64/boot/dts/apm-storm.dtsi index 87d3205e98d545fefb72d17f84ba51efef35a5b5..295c72d52a1f7206f00a29305328f4f19a5f0705 100644 --- a/arch/arm64/boot/dts/apm-storm.dtsi +++ b/arch/arm64/boot/dts/apm-storm.dtsi @@ -176,6 +176,16 @@ clock-output-names = "menetclk"; }; + sge0clk: sge0clk@1f21c000 { + compatible = "apm,xgene-device-clock"; + #clock-cells = <1>; + clocks = <&socplldiv2 0>; + reg = <0x0 0x1f21c000 0x0 0x1000>; + reg-names = "csr-reg"; + csr-mask = <0x3>; + clock-output-names = "sge0clk"; + }; + xge0clk: xge0clk@1f61c000 { compatible = "apm,xgene-device-clock"; #clock-cells = <1>; @@ -611,6 +621,20 @@ }; }; + sgenet0: ethernet@1f210000 { + compatible = "apm,xgene-enet"; + status = "disabled"; + reg = <0x0 0x1f210000 0x0 0x10000>, + <0x0 0x1f200000 0x0 0X10000>, + <0x0 0x1B000000 0x0 0X20000>; + reg-names = "enet_csr", "ring_csr", "ring_cmd"; + interrupts = <0x0 0xA0 0x4>; + dma-coherent; + clocks = <&sge0clk 0>; + local-mac-address = [00 00 00 00 00 00]; + phy-connection-type = "sgmii"; + }; + xgenet: ethernet@1f610000 { compatible = "apm,xgene-enet"; status = "disabled"; diff --git a/arch/arm64/include/asm/Kbuild b/arch/arm64/include/asm/Kbuild index 774a7c85e70f8d75ae29a7268377dff3622e4158..dc770bd4f5a53c0e9a32d97817c55d6698ec87c1 100644 --- a/arch/arm64/include/asm/Kbuild +++ b/arch/arm64/include/asm/Kbuild @@ -9,6 +9,7 @@ generic-y += current.h generic-y += delay.h generic-y += div64.h generic-y += dma.h +generic-y += dma-contiguous.h generic-y += early_ioremap.h generic-y += emergency-restart.h generic-y += errno.h diff --git a/arch/arm64/include/asm/dma-contiguous.h b/arch/arm64/include/asm/dma-contiguous.h deleted file mode 100644 index 14c4c0ca7f2a217851219355040c017cfe4a87a2..0000000000000000000000000000000000000000 --- a/arch/arm64/include/asm/dma-contiguous.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2013, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _ASM_DMA_CONTIGUOUS_H -#define _ASM_DMA_CONTIGUOUS_H - -#ifdef __KERNEL__ -#ifdef CONFIG_DMA_CMA - -#include - -static inline void -dma_contiguous_early_fixup(phys_addr_t base, unsigned long size) { } - -#endif -#endif - -#endif diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h index a030d163840bf02e9eccd4828f54743dc4b8363b..0caf7a59f6a191327f9f662a67cb94e638426e25 100644 --- a/arch/arm64/include/asm/kvm_mmu.h +++ b/arch/arm64/include/asm/kvm_mmu.h @@ -41,6 +41,18 @@ */ #define TRAMPOLINE_VA (HYP_PAGE_OFFSET_MASK & PAGE_MASK) +/* + * KVM_MMU_CACHE_MIN_PAGES is the number of stage2 page table translation + * levels in addition to the PGD and potentially the PUD which are + * pre-allocated (we pre-allocate the fake PGD and the PUD when the Stage-2 + * tables use one level of tables less than the kernel. + */ +#ifdef CONFIG_ARM64_64K_PAGES +#define KVM_MMU_CACHE_MIN_PAGES 1 +#else +#define KVM_MMU_CACHE_MIN_PAGES 2 +#endif + #ifdef __ASSEMBLY__ /* @@ -53,6 +65,7 @@ #else +#include #include #include @@ -65,10 +78,6 @@ #define KVM_PHYS_SIZE (1UL << KVM_PHYS_SHIFT) #define KVM_PHYS_MASK (KVM_PHYS_SIZE - 1UL) -/* Make sure we get the right size, and thus the right alignment */ -#define PTRS_PER_S2_PGD (1 << (KVM_PHYS_SHIFT - PGDIR_SHIFT)) -#define S2_PGD_ORDER get_order(PTRS_PER_S2_PGD * sizeof(pgd_t)) - int create_hyp_mappings(void *from, void *to); int create_hyp_io_mappings(void *from, void *to, phys_addr_t); void free_boot_hyp_pgd(void); @@ -77,7 +86,7 @@ void free_hyp_pgds(void); int kvm_alloc_stage2_pgd(struct kvm *kvm); void kvm_free_stage2_pgd(struct kvm *kvm); int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa, - phys_addr_t pa, unsigned long size); + phys_addr_t pa, unsigned long size, bool writable); int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run); @@ -93,6 +102,7 @@ void kvm_clear_hyp_idmap(void); #define kvm_set_pmd(pmdp, pmd) set_pmd(pmdp, pmd) static inline void kvm_clean_pgd(pgd_t *pgd) {} +static inline void kvm_clean_pmd(pmd_t *pmd) {} static inline void kvm_clean_pmd_entry(pmd_t *pmd) {} static inline void kvm_clean_pte(pte_t *pte) {} static inline void kvm_clean_pte_entry(pte_t *pte) {} @@ -111,19 +121,116 @@ static inline void kvm_set_s2pmd_writable(pmd_t *pmd) #define kvm_pud_addr_end(addr, end) pud_addr_end(addr, end) #define kvm_pmd_addr_end(addr, end) pmd_addr_end(addr, end) +/* + * In the case where PGDIR_SHIFT is larger than KVM_PHYS_SHIFT, we can address + * the entire IPA input range with a single pgd entry, and we would only need + * one pgd entry. Note that in this case, the pgd is actually not used by + * the MMU for Stage-2 translations, but is merely a fake pgd used as a data + * structure for the kernel pgtable macros to work. + */ +#if PGDIR_SHIFT > KVM_PHYS_SHIFT +#define PTRS_PER_S2_PGD_SHIFT 0 +#else +#define PTRS_PER_S2_PGD_SHIFT (KVM_PHYS_SHIFT - PGDIR_SHIFT) +#endif +#define PTRS_PER_S2_PGD (1 << PTRS_PER_S2_PGD_SHIFT) +#define S2_PGD_ORDER get_order(PTRS_PER_S2_PGD * sizeof(pgd_t)) + +/* + * If we are concatenating first level stage-2 page tables, we would have less + * than or equal to 16 pointers in the fake PGD, because that's what the + * architecture allows. In this case, (4 - CONFIG_ARM64_PGTABLE_LEVELS) + * represents the first level for the host, and we add 1 to go to the next + * level (which uses contatenation) for the stage-2 tables. + */ +#if PTRS_PER_S2_PGD <= 16 +#define KVM_PREALLOC_LEVEL (4 - CONFIG_ARM64_PGTABLE_LEVELS + 1) +#else +#define KVM_PREALLOC_LEVEL (0) +#endif + +/** + * kvm_prealloc_hwpgd - allocate inital table for VTTBR + * @kvm: The KVM struct pointer for the VM. + * @pgd: The kernel pseudo pgd + * + * When the kernel uses more levels of page tables than the guest, we allocate + * a fake PGD and pre-populate it to point to the next-level page table, which + * will be the real initial page table pointed to by the VTTBR. + * + * When KVM_PREALLOC_LEVEL==2, we allocate a single page for the PMD and + * the kernel will use folded pud. When KVM_PREALLOC_LEVEL==1, we + * allocate 2 consecutive PUD pages. + */ +static inline int kvm_prealloc_hwpgd(struct kvm *kvm, pgd_t *pgd) +{ + unsigned int i; + unsigned long hwpgd; + + if (KVM_PREALLOC_LEVEL == 0) + return 0; + + hwpgd = __get_free_pages(GFP_KERNEL | __GFP_ZERO, PTRS_PER_S2_PGD_SHIFT); + if (!hwpgd) + return -ENOMEM; + + for (i = 0; i < PTRS_PER_S2_PGD; i++) { + if (KVM_PREALLOC_LEVEL == 1) + pgd_populate(NULL, pgd + i, + (pud_t *)hwpgd + i * PTRS_PER_PUD); + else if (KVM_PREALLOC_LEVEL == 2) + pud_populate(NULL, pud_offset(pgd, 0) + i, + (pmd_t *)hwpgd + i * PTRS_PER_PMD); + } + + return 0; +} + +static inline void *kvm_get_hwpgd(struct kvm *kvm) +{ + pgd_t *pgd = kvm->arch.pgd; + pud_t *pud; + + if (KVM_PREALLOC_LEVEL == 0) + return pgd; + + pud = pud_offset(pgd, 0); + if (KVM_PREALLOC_LEVEL == 1) + return pud; + + BUG_ON(KVM_PREALLOC_LEVEL != 2); + return pmd_offset(pud, 0); +} + +static inline void kvm_free_hwpgd(struct kvm *kvm) +{ + if (KVM_PREALLOC_LEVEL > 0) { + unsigned long hwpgd = (unsigned long)kvm_get_hwpgd(kvm); + free_pages(hwpgd, PTRS_PER_S2_PGD_SHIFT); + } +} + static inline bool kvm_page_empty(void *ptr) { struct page *ptr_page = virt_to_page(ptr); return page_count(ptr_page) == 1; } -#define kvm_pte_table_empty(ptep) kvm_page_empty(ptep) -#ifndef CONFIG_ARM64_64K_PAGES -#define kvm_pmd_table_empty(pmdp) kvm_page_empty(pmdp) +#define kvm_pte_table_empty(kvm, ptep) kvm_page_empty(ptep) + +#ifdef __PAGETABLE_PMD_FOLDED +#define kvm_pmd_table_empty(kvm, pmdp) (0) +#else +#define kvm_pmd_table_empty(kvm, pmdp) \ + (kvm_page_empty(pmdp) && (!(kvm) || KVM_PREALLOC_LEVEL < 2)) +#endif + +#ifdef __PAGETABLE_PUD_FOLDED +#define kvm_pud_table_empty(kvm, pudp) (0) #else -#define kvm_pmd_table_empty(pmdp) (0) +#define kvm_pud_table_empty(kvm, pudp) \ + (kvm_page_empty(pudp) && (!(kvm) || KVM_PREALLOC_LEVEL < 1)) #endif -#define kvm_pud_table_empty(pudp) (0) struct kvm; diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index cefd3e825612640a061da464ac43e2a63cbefcea..41a43bf26492b6558bbf1e78f7747a8b4a39d05d 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -79,7 +79,7 @@ extern void __pgd_error(const char *file, int line, unsigned long val); #define PAGE_HYP_DEVICE __pgprot(PROT_DEVICE_nGnRE | PTE_HYP) #define PAGE_S2 __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_NORMAL) | PTE_S2_RDONLY) -#define PAGE_S2_DEVICE __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_DEVICE_nGnRE) | PTE_S2_RDWR | PTE_UXN) +#define PAGE_S2_DEVICE __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_DEVICE_nGnRE) | PTE_S2_RDONLY | PTE_UXN) #define PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_TYPE_MASK) | PTE_PROT_NONE | PTE_PXN | PTE_UXN) #define PAGE_SHARED __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_WRITE) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index fe63ac5e9bf5582cb4e494262a9d714d4b6758a7..8a4ae8e73213a26add3da24f8a0ea2a8f17ff4ed 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -1120,8 +1120,8 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs) if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) trace_sys_enter(regs, regs->syscallno); - audit_syscall_entry(syscall_get_arch(), regs->syscallno, - regs->orig_x0, regs->regs[1], regs->regs[2], regs->regs[3]); + audit_syscall_entry(regs->syscallno, regs->orig_x0, regs->regs[1], + regs->regs[2], regs->regs[3]); return regs->syscallno; } diff --git a/arch/arm64/kvm/vgic-v2-switch.S b/arch/arm64/kvm/vgic-v2-switch.S index ae211772f99177e7653050525652806ed4bd0159..f002fe1c37002aff928e834a3c6c93008a3aa7d1 100644 --- a/arch/arm64/kvm/vgic-v2-switch.S +++ b/arch/arm64/kvm/vgic-v2-switch.S @@ -67,10 +67,14 @@ CPU_BE( rev w11, w11 ) str w4, [x3, #VGIC_V2_CPU_HCR] str w5, [x3, #VGIC_V2_CPU_VMCR] str w6, [x3, #VGIC_V2_CPU_MISR] - str w7, [x3, #VGIC_V2_CPU_EISR] - str w8, [x3, #(VGIC_V2_CPU_EISR + 4)] - str w9, [x3, #VGIC_V2_CPU_ELRSR] - str w10, [x3, #(VGIC_V2_CPU_ELRSR + 4)] +CPU_LE( str w7, [x3, #VGIC_V2_CPU_EISR] ) +CPU_LE( str w8, [x3, #(VGIC_V2_CPU_EISR + 4)] ) +CPU_LE( str w9, [x3, #VGIC_V2_CPU_ELRSR] ) +CPU_LE( str w10, [x3, #(VGIC_V2_CPU_ELRSR + 4)] ) +CPU_BE( str w7, [x3, #(VGIC_V2_CPU_EISR + 4)] ) +CPU_BE( str w8, [x3, #VGIC_V2_CPU_EISR] ) +CPU_BE( str w9, [x3, #(VGIC_V2_CPU_ELRSR + 4)] ) +CPU_BE( str w10, [x3, #VGIC_V2_CPU_ELRSR] ) str w11, [x3, #VGIC_V2_CPU_APR] /* Clear GICH_HCR */ diff --git a/arch/avr32/kernel/kprobes.c b/arch/avr32/kernel/kprobes.c index f820e9f25520655ae0ddb756928be4d2aba35eb6..a94ece4a72c84394fb3a64ef4b6d729770a7cc77 100644 --- a/arch/avr32/kernel/kprobes.c +++ b/arch/avr32/kernel/kprobes.c @@ -104,7 +104,7 @@ static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs) static void __kprobes set_current_kprobe(struct kprobe *p) { - __get_cpu_var(current_kprobe) = p; + __this_cpu_write(current_kprobe, p); } static int __kprobes kprobe_handler(struct pt_regs *regs) diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index db85b5ec3351bb5b937c06f0a32153aa2be31592..37b75602adf60e382587dd3b118eaae5ad03d02e 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -7,7 +7,7 @@ */ #include #include -#include +#include #include #include #include @@ -1356,10 +1356,10 @@ at32_add_device_mci(unsigned int id, struct mci_platform_data *data) goto fail; slave->sdata.dma_dev = &dw_dmac0_device.dev; - slave->sdata.cfg_hi = (DWC_CFGH_SRC_PER(0) - | DWC_CFGH_DST_PER(1)); - slave->sdata.cfg_lo &= ~(DWC_CFGL_HS_DST_POL - | DWC_CFGL_HS_SRC_POL); + slave->sdata.src_id = 0; + slave->sdata.dst_id = 1; + slave->sdata.src_master = 1; + slave->sdata.dst_master = 0; data->dma_slave = slave; @@ -2052,8 +2052,7 @@ at32_add_device_ac97c(unsigned int id, struct ac97c_platform_data *data, /* Check if DMA slave interface for capture should be configured. */ if (flags & AC97C_CAPTURE) { rx_dws->dma_dev = &dw_dmac0_device.dev; - rx_dws->cfg_hi = DWC_CFGH_SRC_PER(3); - rx_dws->cfg_lo &= ~(DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL); + rx_dws->src_id = 3; rx_dws->src_master = 0; rx_dws->dst_master = 1; } @@ -2061,8 +2060,7 @@ at32_add_device_ac97c(unsigned int id, struct ac97c_platform_data *data, /* Check if DMA slave interface for playback should be configured. */ if (flags & AC97C_PLAYBACK) { tx_dws->dma_dev = &dw_dmac0_device.dev; - tx_dws->cfg_hi = DWC_CFGH_DST_PER(4); - tx_dws->cfg_lo &= ~(DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL); + tx_dws->dst_id = 4; tx_dws->src_master = 0; tx_dws->dst_master = 1; } @@ -2134,8 +2132,7 @@ at32_add_device_abdac(unsigned int id, struct atmel_abdac_pdata *data) dws = &data->dws; dws->dma_dev = &dw_dmac0_device.dev; - dws->cfg_hi = DWC_CFGH_DST_PER(2); - dws->cfg_lo &= ~(DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL); + dws->dst_id = 2; dws->src_master = 0; dws->dst_master = 1; diff --git a/arch/avr32/mach-at32ap/include/mach/atmel-mci.h b/arch/avr32/mach-at32ap/include/mach/atmel-mci.h index 4bba58561d5c887a18ec0f4032fa507df5669c2a..11d7f4b28dc89fbfb42d61978d59898e0ac18875 100644 --- a/arch/avr32/mach-at32ap/include/mach/atmel-mci.h +++ b/arch/avr32/mach-at32ap/include/mach/atmel-mci.h @@ -1,7 +1,7 @@ #ifndef __MACH_ATMEL_MCI_H #define __MACH_ATMEL_MCI_H -#include +#include /** * struct mci_dma_data - DMA data for MCI interface diff --git a/arch/blackfin/include/asm/ipipe.h b/arch/blackfin/include/asm/ipipe.h index 17b5e92e3bc683e1382d926297484582725a0273..fe1160fbff912140b19db436d16380d4c4605181 100644 --- a/arch/blackfin/include/asm/ipipe.h +++ b/arch/blackfin/include/asm/ipipe.h @@ -157,7 +157,7 @@ static inline unsigned long __ipipe_ffnz(unsigned long ul) } #define __ipipe_do_root_xirq(ipd, irq) \ - ((ipd)->irqs[irq].handler(irq, &__raw_get_cpu_var(__ipipe_tick_regs))) + ((ipd)->irqs[irq].handler(irq, raw_cpu_ptr(&__ipipe_tick_regs))) #define __ipipe_run_irqtail(irq) /* Must be a macro */ \ do { \ diff --git a/arch/blackfin/kernel/perf_event.c b/arch/blackfin/kernel/perf_event.c index ea2032013cc266a25363a52f6f043e830d243cb2..1e9c8b0bf48666cfba1ee19d8731eb71cf68a77e 100644 --- a/arch/blackfin/kernel/perf_event.c +++ b/arch/blackfin/kernel/perf_event.c @@ -300,7 +300,7 @@ static void bfin_perf_event_update(struct perf_event *event, static void bfin_pmu_stop(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc = &event->hw; int idx = hwc->idx; @@ -318,7 +318,7 @@ static void bfin_pmu_stop(struct perf_event *event, int flags) static void bfin_pmu_start(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc = &event->hw; int idx = hwc->idx; @@ -335,7 +335,7 @@ static void bfin_pmu_start(struct perf_event *event, int flags) static void bfin_pmu_del(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); bfin_pmu_stop(event, PERF_EF_UPDATE); __clear_bit(event->hw.idx, cpuc->used_mask); @@ -345,7 +345,7 @@ static void bfin_pmu_del(struct perf_event *event, int flags) static int bfin_pmu_add(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc = &event->hw; int idx = hwc->idx; int ret = -EAGAIN; @@ -421,7 +421,7 @@ static int bfin_pmu_event_init(struct perf_event *event) static void bfin_pmu_enable(struct pmu *pmu) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct perf_event *event; struct hw_perf_event *hwc; int i; diff --git a/arch/blackfin/mach-common/ints-priority.c b/arch/blackfin/mach-common/ints-priority.c index 694619365265a7792bb457c4fe48ebd484e53bca..dd2af74aff804afb69cdacecf67e3d4f35b02690 100644 --- a/arch/blackfin/mach-common/ints-priority.c +++ b/arch/blackfin/mach-common/ints-priority.c @@ -1309,12 +1309,12 @@ asmlinkage int __ipipe_grab_irq(int vec, struct pt_regs *regs) bfin_write_TIMER_STATUS(1); /* Latch TIMIL0 */ #endif /* This is basically what we need from the register frame. */ - __raw_get_cpu_var(__ipipe_tick_regs).ipend = regs->ipend; - __raw_get_cpu_var(__ipipe_tick_regs).pc = regs->pc; + __this_cpu_write(__ipipe_tick_regs.ipend, regs->ipend); + __this_cpu_write(__ipipe_tick_regs.pc, regs->pc); if (this_domain != ipipe_root_domain) - __raw_get_cpu_var(__ipipe_tick_regs).ipend &= ~0x10; + __this_cpu_and(__ipipe_tick_regs.ipend, ~0x10); else - __raw_get_cpu_var(__ipipe_tick_regs).ipend |= 0x10; + __this_cpu_or(__ipipe_tick_regs.ipend, 0x10); } /* diff --git a/arch/blackfin/mach-common/smp.c b/arch/blackfin/mach-common/smp.c index ba6c30d8534d0dec77cac409788aa37c03772748..8ad3e90cc8fc4c7780173003bc531c59624adaa4 100644 --- a/arch/blackfin/mach-common/smp.c +++ b/arch/blackfin/mach-common/smp.c @@ -146,7 +146,7 @@ static irqreturn_t ipi_handler_int1(int irq, void *dev_instance) platform_clear_ipi(cpu, IRQ_SUPPLE_1); smp_rmb(); - bfin_ipi_data = &__get_cpu_var(bfin_ipi); + bfin_ipi_data = this_cpu_ptr(&bfin_ipi); while ((pending = atomic_xchg(&bfin_ipi_data->bits, 0)) != 0) { msg = 0; do { diff --git a/arch/frv/mm/extable.c b/arch/frv/mm/extable.c index 6aea124f574db5a19d6dac8ec3aafff8d4df7246..2fb9b3ab57b9d624883fb5dd8d53944b12bb1556 100644 --- a/arch/frv/mm/extable.c +++ b/arch/frv/mm/extable.c @@ -6,8 +6,6 @@ #include #include -extern const struct exception_table_entry __attribute__((aligned(8))) __start___ex_table[]; -extern const struct exception_table_entry __attribute__((aligned(8))) __stop___ex_table[]; extern const void __memset_end, __memset_user_error_lr, __memset_user_error_handler; extern const void __memcpy_end, __memcpy_user_error_lr, __memcpy_user_error_handler; extern spinlock_t modlist_lock; diff --git a/arch/ia64/include/asm/hw_irq.h b/arch/ia64/include/asm/hw_irq.h index 029bab36cd91c0e5949a7ad04ad68264a4be81ad..668786e84af8ae1eee5f0ac97d8926889fc2cb29 100644 --- a/arch/ia64/include/asm/hw_irq.h +++ b/arch/ia64/include/asm/hw_irq.h @@ -159,7 +159,7 @@ static inline ia64_vector __ia64_irq_to_vector(int irq) static inline unsigned int __ia64_local_vector_to_irq (ia64_vector vec) { - return __get_cpu_var(vector_irq)[vec]; + return __this_cpu_read(vector_irq[vec]); } #endif diff --git a/arch/ia64/include/asm/sections.h b/arch/ia64/include/asm/sections.h index 1a873b36a4a1cd75f94bb196f1a7997d9ef621f3..2ab2003698ef6acd2fa7e5ffd5cabb31be3317b4 100644 --- a/arch/ia64/include/asm/sections.h +++ b/arch/ia64/include/asm/sections.h @@ -10,7 +10,7 @@ #include #include -extern char __per_cpu_start[], __per_cpu_end[], __phys_per_cpu_start[]; +extern char __phys_per_cpu_start[]; #ifdef CONFIG_SMP extern char __cpu0_per_cpu[]; #endif diff --git a/arch/ia64/include/asm/sn/arch.h b/arch/ia64/include/asm/sn/arch.h index 7caa1f44cd95d923b21116c7e727c3e787790058..31eb784866f899331d9a0afecffe76a2d775eda3 100644 --- a/arch/ia64/include/asm/sn/arch.h +++ b/arch/ia64/include/asm/sn/arch.h @@ -57,7 +57,7 @@ struct sn_hub_info_s { u16 nasid_bitmask; }; DECLARE_PER_CPU(struct sn_hub_info_s, __sn_hub_info); -#define sn_hub_info (&__get_cpu_var(__sn_hub_info)) +#define sn_hub_info this_cpu_ptr(&__sn_hub_info) #define is_shub2() (sn_hub_info->shub2) #define is_shub1() (sn_hub_info->shub2 == 0) @@ -72,7 +72,7 @@ DECLARE_PER_CPU(struct sn_hub_info_s, __sn_hub_info); * cpu. */ DECLARE_PER_CPU(short, __sn_cnodeid_to_nasid[MAX_COMPACT_NODES]); -#define sn_cnodeid_to_nasid (&__get_cpu_var(__sn_cnodeid_to_nasid[0])) +#define sn_cnodeid_to_nasid this_cpu_ptr(&__sn_cnodeid_to_nasid[0]) extern u8 sn_partition_id; diff --git a/arch/ia64/include/asm/sn/nodepda.h b/arch/ia64/include/asm/sn/nodepda.h index ee118b901de479e065ccc1cf361c1db141680f4c..7c8b4710f071d5385e6865cdba3d6c841081192a 100644 --- a/arch/ia64/include/asm/sn/nodepda.h +++ b/arch/ia64/include/asm/sn/nodepda.h @@ -70,7 +70,7 @@ typedef struct nodepda_s nodepda_t; */ DECLARE_PER_CPU(struct nodepda_s *, __sn_nodepda); -#define sn_nodepda (__get_cpu_var(__sn_nodepda)) +#define sn_nodepda __this_cpu_read(__sn_nodepda) #define NODEPDA(cnodeid) (sn_nodepda->pernode_pdaindr[cnodeid]) /* diff --git a/arch/ia64/include/asm/switch_to.h b/arch/ia64/include/asm/switch_to.h index d38c7ea5eea5d129a32132583c2bbcb8a319bcb9..e8f3585e7e7a9b5133c3f7f9c71b2d7476e848c9 100644 --- a/arch/ia64/include/asm/switch_to.h +++ b/arch/ia64/include/asm/switch_to.h @@ -32,7 +32,7 @@ extern void ia64_load_extra (struct task_struct *task); #ifdef CONFIG_PERFMON DECLARE_PER_CPU(unsigned long, pfm_syst_info); -# define PERFMON_IS_SYSWIDE() (__get_cpu_var(pfm_syst_info) & 0x1) +# define PERFMON_IS_SYSWIDE() (__this_cpu_read(pfm_syst_info) & 0x1) #else # define PERFMON_IS_SYSWIDE() (0) #endif diff --git a/arch/ia64/include/asm/syscall.h b/arch/ia64/include/asm/syscall.h index a7ff1c6ab0689af9e547c5a138df286423b9788b..1d0b875fec44fc0fd8a70876e8ee6191f66f6005 100644 --- a/arch/ia64/include/asm/syscall.h +++ b/arch/ia64/include/asm/syscall.h @@ -13,6 +13,7 @@ #ifndef _ASM_SYSCALL_H #define _ASM_SYSCALL_H 1 +#include #include #include @@ -79,4 +80,9 @@ static inline void syscall_set_arguments(struct task_struct *task, ia64_syscall_get_set_arguments(task, regs, i, n, args, 1); } + +static inline int syscall_get_arch(void) +{ + return AUDIT_ARCH_IA64; +} #endif /* _ASM_SYSCALL_H */ diff --git a/arch/ia64/include/asm/unistd.h b/arch/ia64/include/asm/unistd.h index 10a14ead70b9d9fd627b416c8567008d1aebc606..f3b51b57740af91e097a7b4b37b4067b5873cd44 100644 --- a/arch/ia64/include/asm/unistd.h +++ b/arch/ia64/include/asm/unistd.h @@ -11,7 +11,7 @@ -#define NR_syscalls 317 /* length of syscall table */ +#define NR_syscalls 318 /* length of syscall table */ /* * The following defines stop scripts/checksyscalls.sh from complaining about diff --git a/arch/ia64/include/asm/uv/uv_hub.h b/arch/ia64/include/asm/uv/uv_hub.h index 53e9dfacd07393a57b9ae9ab114c2e4b5596f4dc..2a88c7204e524fc472bcc6919ad6c9d1d29ffbff 100644 --- a/arch/ia64/include/asm/uv/uv_hub.h +++ b/arch/ia64/include/asm/uv/uv_hub.h @@ -108,7 +108,7 @@ struct uv_hub_info_s { unsigned char n_val; }; DECLARE_PER_CPU(struct uv_hub_info_s, __uv_hub_info); -#define uv_hub_info (&__get_cpu_var(__uv_hub_info)) +#define uv_hub_info this_cpu_ptr(&__uv_hub_info) #define uv_cpu_hub_info(cpu) (&per_cpu(__uv_hub_info, cpu)) /* diff --git a/arch/ia64/include/uapi/asm/unistd.h b/arch/ia64/include/uapi/asm/unistd.h index 18026b2eb5823c86ab0455088e9b3b1b0c409393..4c2240c1b0cb4b81e219631750c4e61f82ffb750 100644 --- a/arch/ia64/include/uapi/asm/unistd.h +++ b/arch/ia64/include/uapi/asm/unistd.h @@ -330,5 +330,6 @@ #define __NR_renameat2 1338 #define __NR_getrandom 1339 #define __NR_memfd_create 1340 +#define __NR_bpf 1341 #endif /* _UAPI_ASM_IA64_UNISTD_H */ diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index 01edf242eb296443293fc8b1ee178d72d0a89f55..f5e96dffc63c3d0ce54399759ceb9a5fefacde15 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -1778,6 +1778,7 @@ sys_call_table: data8 sys_renameat2 data8 sys_getrandom data8 sys_memfd_create // 1340 + data8 sys_bpf .org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls #endif /* __IA64_ASM_PARAVIRTUALIZED_NATIVE */ diff --git a/arch/ia64/kernel/irq.c b/arch/ia64/kernel/irq.c index f2c41828113050c19f0f09ce6d97beb733ebcd3e..812a1e6b317966610811bb10d97e7c1cdab84a80 100644 --- a/arch/ia64/kernel/irq.c +++ b/arch/ia64/kernel/irq.c @@ -42,7 +42,7 @@ ia64_vector __ia64_irq_to_vector(int irq) unsigned int __ia64_local_vector_to_irq (ia64_vector vec) { - return __get_cpu_var(vector_irq)[vec]; + return __this_cpu_read(vector_irq[vec]); } #endif diff --git a/arch/ia64/kernel/irq_ia64.c b/arch/ia64/kernel/irq_ia64.c index 03ea78ed64a93384f8d4730720b99fa34e02b2d1..698d8fefde6c2591071be0dd0ccc7d64bca8703e 100644 --- a/arch/ia64/kernel/irq_ia64.c +++ b/arch/ia64/kernel/irq_ia64.c @@ -330,7 +330,7 @@ static irqreturn_t smp_irq_move_cleanup_interrupt(int irq, void *dev_id) int irq; struct irq_desc *desc; struct irq_cfg *cfg; - irq = __get_cpu_var(vector_irq)[vector]; + irq = __this_cpu_read(vector_irq[vector]); if (irq < 0) continue; @@ -344,7 +344,7 @@ static irqreturn_t smp_irq_move_cleanup_interrupt(int irq, void *dev_id) goto unlock; spin_lock_irqsave(&vector_lock, flags); - __get_cpu_var(vector_irq)[vector] = -1; + __this_cpu_write(vector_irq[vector], -1); cpu_clear(me, vector_table[vector]); spin_unlock_irqrestore(&vector_lock, flags); cfg->move_cleanup_count--; diff --git a/arch/ia64/kernel/kprobes.c b/arch/ia64/kernel/kprobes.c index 074fde49c9e6234f22e3e5b132bcdcfe176e9757..c7c51445c3be32d95ef3704e3b87e3e908b4f9a6 100644 --- a/arch/ia64/kernel/kprobes.c +++ b/arch/ia64/kernel/kprobes.c @@ -396,7 +396,7 @@ static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) { unsigned int i; i = atomic_read(&kcb->prev_kprobe_index); - __get_cpu_var(current_kprobe) = kcb->prev_kprobe[i-1].kp; + __this_cpu_write(current_kprobe, kcb->prev_kprobe[i-1].kp); kcb->kprobe_status = kcb->prev_kprobe[i-1].status; atomic_sub(1, &kcb->prev_kprobe_index); } @@ -404,7 +404,7 @@ static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) static void __kprobes set_current_kprobe(struct kprobe *p, struct kprobe_ctlblk *kcb) { - __get_cpu_var(current_kprobe) = p; + __this_cpu_write(current_kprobe, p); } static void kretprobe_trampoline(void) @@ -823,7 +823,7 @@ static int __kprobes pre_kprobes_handler(struct die_args *args) /* * jprobe instrumented function just completed */ - p = __get_cpu_var(current_kprobe); + p = __this_cpu_read(current_kprobe); if (p->break_handler && p->break_handler(p, regs)) { goto ss_probe; } diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index db7b36bb068b8d306042028560dbbaafcbf7664f..8bfd36af46f8e216e7330cdc443be16ceb9520c7 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c @@ -1341,7 +1341,7 @@ ia64_mca_handler(struct pt_regs *regs, struct switch_stack *sw, ia64_mlogbuf_finish(1); } - if (__get_cpu_var(ia64_mca_tr_reload)) { + if (__this_cpu_read(ia64_mca_tr_reload)) { mca_insert_tr(0x1); /*Reload dynamic itrs*/ mca_insert_tr(0x2); /*Reload dynamic itrs*/ } @@ -1868,14 +1868,14 @@ ia64_mca_cpu_init(void *cpu_data) "MCA", cpu); format_mca_init_stack(data, offsetof(struct ia64_mca_cpu, init_stack), "INIT", cpu); - __get_cpu_var(ia64_mca_data) = __per_cpu_mca[cpu] = __pa(data); + __this_cpu_write(ia64_mca_data, (__per_cpu_mca[cpu] = __pa(data))); /* * Stash away a copy of the PTE needed to map the per-CPU page. * We may need it during MCA recovery. */ - __get_cpu_var(ia64_mca_per_cpu_pte) = - pte_val(mk_pte_phys(__pa(cpu_data), PAGE_KERNEL)); + __this_cpu_write(ia64_mca_per_cpu_pte, + pte_val(mk_pte_phys(__pa(cpu_data), PAGE_KERNEL))); /* * Also, stash away a copy of the PAL address and the PTE @@ -1884,10 +1884,10 @@ ia64_mca_cpu_init(void *cpu_data) pal_vaddr = efi_get_pal_addr(); if (!pal_vaddr) return; - __get_cpu_var(ia64_mca_pal_base) = - GRANULEROUNDDOWN((unsigned long) pal_vaddr); - __get_cpu_var(ia64_mca_pal_pte) = pte_val(mk_pte_phys(__pa(pal_vaddr), - PAGE_KERNEL)); + __this_cpu_write(ia64_mca_pal_base, + GRANULEROUNDDOWN((unsigned long) pal_vaddr)); + __this_cpu_write(ia64_mca_pal_pte, pte_val(mk_pte_phys(__pa(pal_vaddr), + PAGE_KERNEL))); } static void ia64_mca_cmc_vector_adjust(void *dummy) diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c index deed6fa96bb054c50c6a12bdf83fe57028a5eb1d..b51514957620bef1d703ddf450700750b1dfd077 100644 --- a/arch/ia64/kernel/process.c +++ b/arch/ia64/kernel/process.c @@ -215,7 +215,7 @@ static inline void play_dead(void) unsigned int this_cpu = smp_processor_id(); /* Ack it */ - __get_cpu_var(cpu_state) = CPU_DEAD; + __this_cpu_write(cpu_state, CPU_DEAD); max_xtp(); local_irq_disable(); @@ -273,7 +273,7 @@ ia64_save_extra (struct task_struct *task) if ((task->thread.flags & IA64_THREAD_PM_VALID) != 0) pfm_save_regs(task); - info = __get_cpu_var(pfm_syst_info); + info = __this_cpu_read(pfm_syst_info); if (info & PFM_CPUINFO_SYST_WIDE) pfm_syst_wide_update_task(task, info, 0); #endif @@ -293,7 +293,7 @@ ia64_load_extra (struct task_struct *task) if ((task->thread.flags & IA64_THREAD_PM_VALID) != 0) pfm_load_regs(task); - info = __get_cpu_var(pfm_syst_info); + info = __this_cpu_read(pfm_syst_info); if (info & PFM_CPUINFO_SYST_WIDE) pfm_syst_wide_update_task(task, info, 1); #endif diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index b7a5fffe0924e03c860f50afcebc5bc0fc4ea7a3..6f54d511cc509a03ac079871b6979f03e6b53bc8 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -1219,7 +1219,7 @@ syscall_trace_enter (long arg0, long arg1, long arg2, long arg3, ia64_sync_krbs(); - audit_syscall_entry(AUDIT_ARCH_IA64, regs.r15, arg0, arg1, arg2, arg3); + audit_syscall_entry(regs.r15, arg0, arg1, arg2, arg3); return 0; } diff --git a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c index d3636e67a98e8281f44c0038c762838e942b723f..6f7d4a4dcf24d1a2b207b24f2ecc42e5f6f99d0f 100644 --- a/arch/ia64/kernel/traps.c +++ b/arch/ia64/kernel/traps.c @@ -299,7 +299,7 @@ handle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr) if (!(current->thread.flags & IA64_THREAD_FPEMU_NOPRINT)) { unsigned long count, current_jiffies = jiffies; - struct fpu_swa_msg *cp = &__get_cpu_var(cpulast); + struct fpu_swa_msg *cp = this_cpu_ptr(&cpulast); if (unlikely(current_jiffies > cp->time)) cp->count = 0; diff --git a/arch/ia64/sn/kernel/setup.c b/arch/ia64/sn/kernel/setup.c index 36182c84363ce914d15f34a5be72afdc6f580b46..5f6b6b48c1d58567b21ba3680969fb53f52195fa 100644 --- a/arch/ia64/sn/kernel/setup.c +++ b/arch/ia64/sn/kernel/setup.c @@ -629,7 +629,7 @@ void sn_cpu_init(void) cnode = nasid_to_cnodeid(nasid); - sn_nodepda = nodepdaindr[cnode]; + __this_cpu_write(__sn_nodepda, nodepdaindr[cnode]); pda->led_address = (typeof(pda->led_address)) (LED0 + (slice << LED_CPU_SHIFT)); diff --git a/arch/ia64/sn/kernel/sn2/sn2_smp.c b/arch/ia64/sn/kernel/sn2/sn2_smp.c index 68c845411624a8699e9c6342ccf6b7bf90b4d580..f9c8d9fc593985b7cf027e0186c56b896e551439 100644 --- a/arch/ia64/sn/kernel/sn2/sn2_smp.c +++ b/arch/ia64/sn/kernel/sn2/sn2_smp.c @@ -134,8 +134,8 @@ sn2_ipi_flush_all_tlb(struct mm_struct *mm) itc = ia64_get_itc(); smp_flush_tlb_cpumask(*mm_cpumask(mm)); itc = ia64_get_itc() - itc; - __get_cpu_var(ptcstats).shub_ipi_flushes_itc_clocks += itc; - __get_cpu_var(ptcstats).shub_ipi_flushes++; + __this_cpu_add(ptcstats.shub_ipi_flushes_itc_clocks, itc); + __this_cpu_inc(ptcstats.shub_ipi_flushes); } /** @@ -199,14 +199,14 @@ sn2_global_tlb_purge(struct mm_struct *mm, unsigned long start, start += (1UL << nbits); } while (start < end); ia64_srlz_i(); - __get_cpu_var(ptcstats).ptc_l++; + __this_cpu_inc(ptcstats.ptc_l); preempt_enable(); return; } if (atomic_read(&mm->mm_users) == 1 && mymm) { flush_tlb_mm(mm); - __get_cpu_var(ptcstats).change_rid++; + __this_cpu_inc(ptcstats.change_rid); preempt_enable(); return; } @@ -250,11 +250,11 @@ sn2_global_tlb_purge(struct mm_struct *mm, unsigned long start, spin_lock_irqsave(PTC_LOCK(shub1), flags); itc2 = ia64_get_itc(); - __get_cpu_var(ptcstats).lock_itc_clocks += itc2 - itc; - __get_cpu_var(ptcstats).shub_ptc_flushes++; - __get_cpu_var(ptcstats).nodes_flushed += nix; + __this_cpu_add(ptcstats.lock_itc_clocks, itc2 - itc); + __this_cpu_inc(ptcstats.shub_ptc_flushes); + __this_cpu_add(ptcstats.nodes_flushed, nix); if (!mymm) - __get_cpu_var(ptcstats).shub_ptc_flushes_not_my_mm++; + __this_cpu_inc(ptcstats.shub_ptc_flushes_not_my_mm); if (use_cpu_ptcga && !mymm) { old_rr = ia64_get_rr(start); @@ -299,9 +299,9 @@ sn2_global_tlb_purge(struct mm_struct *mm, unsigned long start, done: itc2 = ia64_get_itc() - itc2; - __get_cpu_var(ptcstats).shub_itc_clocks += itc2; - if (itc2 > __get_cpu_var(ptcstats).shub_itc_clocks_max) - __get_cpu_var(ptcstats).shub_itc_clocks_max = itc2; + __this_cpu_add(ptcstats.shub_itc_clocks, itc2); + if (itc2 > __this_cpu_read(ptcstats.shub_itc_clocks_max)) + __this_cpu_write(ptcstats.shub_itc_clocks_max, itc2); if (old_rr) { ia64_set_rr(start, old_rr); @@ -311,7 +311,7 @@ sn2_global_tlb_purge(struct mm_struct *mm, unsigned long start, spin_unlock_irqrestore(PTC_LOCK(shub1), flags); if (flush_opt == 1 && deadlock) { - __get_cpu_var(ptcstats).deadlocks++; + __this_cpu_inc(ptcstats.deadlocks); sn2_ipi_flush_all_tlb(mm); } @@ -334,7 +334,7 @@ sn2_ptc_deadlock_recovery(short *nasids, short ib, short ie, int mynasid, short nasid, i; unsigned long *piows, zeroval, n; - __get_cpu_var(ptcstats).deadlocks++; + __this_cpu_inc(ptcstats.deadlocks); piows = (unsigned long *) pda->pio_write_status_addr; zeroval = pda->pio_write_status_val; @@ -349,7 +349,7 @@ sn2_ptc_deadlock_recovery(short *nasids, short ib, short ie, int mynasid, ptc1 = CHANGE_NASID(nasid, ptc1); n = sn2_ptc_deadlock_recovery_core(ptc0, data0, ptc1, data1, piows, zeroval); - __get_cpu_var(ptcstats).deadlocks2 += n; + __this_cpu_add(ptcstats.deadlocks2, n); } } diff --git a/arch/m68k/platform/68000/Makefile b/arch/m68k/68000/Makefile similarity index 100% rename from arch/m68k/platform/68000/Makefile rename to arch/m68k/68000/Makefile diff --git a/arch/m68k/platform/68000/bootlogo-vz.h b/arch/m68k/68000/bootlogo-vz.h similarity index 100% rename from arch/m68k/platform/68000/bootlogo-vz.h rename to arch/m68k/68000/bootlogo-vz.h diff --git a/arch/m68k/platform/68000/bootlogo.h b/arch/m68k/68000/bootlogo.h similarity index 100% rename from arch/m68k/platform/68000/bootlogo.h rename to arch/m68k/68000/bootlogo.h diff --git a/arch/m68k/platform/68000/entry.S b/arch/m68k/68000/entry.S similarity index 98% rename from arch/m68k/platform/68000/entry.S rename to arch/m68k/68000/entry.S index 23ac054c6e1ad929fa40e8aa033e51251c96538d..259b3661b614168ff8ab377587c66b1478222218 100644 --- a/arch/m68k/platform/68000/entry.S +++ b/arch/m68k/68000/entry.S @@ -1,5 +1,5 @@ /* - * linux/arch/m68knommu/platform/68328/entry.S + * entry.S -- non-mmu 68000 interrupt and exception entry points * * Copyright (C) 1991, 1992 Linus Torvalds * diff --git a/arch/m68k/platform/68000/head.S b/arch/m68k/68000/head.S similarity index 100% rename from arch/m68k/platform/68000/head.S rename to arch/m68k/68000/head.S diff --git a/arch/m68k/platform/68000/ints.c b/arch/m68k/68000/ints.c similarity index 100% rename from arch/m68k/platform/68000/ints.c rename to arch/m68k/68000/ints.c diff --git a/arch/m68k/platform/68000/m68328.c b/arch/m68k/68000/m68328.c similarity index 100% rename from arch/m68k/platform/68000/m68328.c rename to arch/m68k/68000/m68328.c diff --git a/arch/m68k/platform/68000/m68EZ328.c b/arch/m68k/68000/m68EZ328.c similarity index 100% rename from arch/m68k/platform/68000/m68EZ328.c rename to arch/m68k/68000/m68EZ328.c diff --git a/arch/m68k/platform/68000/m68VZ328.c b/arch/m68k/68000/m68VZ328.c similarity index 100% rename from arch/m68k/platform/68000/m68VZ328.c rename to arch/m68k/68000/m68VZ328.c diff --git a/arch/m68k/platform/68000/romvec.S b/arch/m68k/68000/romvec.S similarity index 100% rename from arch/m68k/platform/68000/romvec.S rename to arch/m68k/68000/romvec.S diff --git a/arch/m68k/platform/68000/timers.c b/arch/m68k/68000/timers.c similarity index 100% rename from arch/m68k/platform/68000/timers.c rename to arch/m68k/68000/timers.c diff --git a/arch/m68k/platform/68360/Makefile b/arch/m68k/68360/Makefile similarity index 81% rename from arch/m68k/platform/68360/Makefile rename to arch/m68k/68360/Makefile index f6f43438304935908f59705dfa24ae7f174e29cb..591ce42df3def52cf03b27d3e3e5794fe6a6d16a 100644 --- a/arch/m68k/platform/68360/Makefile +++ b/arch/m68k/68360/Makefile @@ -1,5 +1,5 @@ # -# Makefile for arch/m68knommu/platform/68360. +# Makefile for 68360 machines. # model-y := ram model-$(CONFIG_ROMKERNEL) := rom diff --git a/arch/m68k/platform/68360/commproc.c b/arch/m68k/68360/commproc.c similarity index 100% rename from arch/m68k/platform/68360/commproc.c rename to arch/m68k/68360/commproc.c diff --git a/arch/m68k/platform/68360/config.c b/arch/m68k/68360/config.c similarity index 98% rename from arch/m68k/platform/68360/config.c rename to arch/m68k/68360/config.c index d493ac43fe3f900fe1b6ecd999e3452e96bdbfd3..17ec416fed9db5e8380d710f4d3b821599ca5da2 100644 --- a/arch/m68k/platform/68360/config.c +++ b/arch/m68k/68360/config.c @@ -1,5 +1,5 @@ /* - * linux/arch/m68knommu/platform/68360/config.c + * config.c - non-mmu 68360 platform initialization code * * Copyright (c) 2000 Michael Leslie * Copyright (C) 1993 Hamish Macdonald diff --git a/arch/m68k/platform/68360/entry.S b/arch/m68k/68360/entry.S similarity index 98% rename from arch/m68k/platform/68360/entry.S rename to arch/m68k/68360/entry.S index 447c33ef37fda3456b5fad22d705f123c91b4f84..22eb3022f9ee83f449e313a7715845df8b52bdad 100644 --- a/arch/m68k/platform/68360/entry.S +++ b/arch/m68k/68360/entry.S @@ -1,5 +1,5 @@ /* - * linux/arch/m68knommu/platform/68360/entry.S + * entry.S - non-mmu 68360 interrupt and exceptions entry points * * Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 2001 SED Systems, a Division of Calian Ltd. diff --git a/arch/m68k/platform/68360/head-ram.S b/arch/m68k/68360/head-ram.S similarity index 99% rename from arch/m68k/platform/68360/head-ram.S rename to arch/m68k/68360/head-ram.S index acd213170d80fe6ae1e74cddd2fcbd90c7b65115..62bc56f41d572311e4846aa28ed374f64631bba8 100644 --- a/arch/m68k/platform/68360/head-ram.S +++ b/arch/m68k/68360/head-ram.S @@ -1,6 +1,5 @@ -/* arch/m68knommu/platform/68360/head-ram.S - * - * Startup code for Motorola 68360 +/* + * head-ram.S - startup code for Motorola 68360 * * Copyright 2001 (C) SED Systems, a Division of Calian Ltd. * Based on: arch/m68knommu/platform/68328/pilot/crt0_rom.S diff --git a/arch/m68k/platform/68360/head-rom.S b/arch/m68k/68360/head-rom.S similarity index 99% rename from arch/m68k/platform/68360/head-rom.S rename to arch/m68k/68360/head-rom.S index dfc756d998861823e48ac2b87a291ea7d04cbbb2..b3a7e40f35e1eecfd17b641a29e042bb15f5a5e8 100644 --- a/arch/m68k/platform/68360/head-rom.S +++ b/arch/m68k/68360/head-rom.S @@ -1,6 +1,5 @@ -/* arch/m68knommu/platform/68360/head-rom.S - * - * Startup code for Motorola 68360 +/* + * head-rom.S - startup code for Motorola 68360 * * Copyright (C) SED Systems, a Division of Calian Ltd. * Based on: arch/m68knommu/platform/68328/pilot/crt0_rom.S diff --git a/arch/m68k/platform/68360/ints.c b/arch/m68k/68360/ints.c similarity index 98% rename from arch/m68k/platform/68360/ints.c rename to arch/m68k/68360/ints.c index 8cd42692331b8d3b6f1f2052dc3f563181917cd4..2360fc046681d72caf89114720ec92b53db351e1 100644 --- a/arch/m68k/platform/68360/ints.c +++ b/arch/m68k/68360/ints.c @@ -1,5 +1,5 @@ /* - * linux/arch/$(ARCH)/platform/$(PLATFORM)/ints.c + * ints.c - first level interrupt handlers * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive diff --git a/arch/m68k/Makefile b/arch/m68k/Makefile index 7f7830f2c5bcb488745c0326e36a1889a733e04b..0b29dcfef69f339407880ac42d42d5d873d2e032 100644 --- a/arch/m68k/Makefile +++ b/arch/m68k/Makefile @@ -92,9 +92,9 @@ endif # head-y := arch/m68k/kernel/head.o head-$(CONFIG_SUN3) := arch/m68k/kernel/sun3-head.o -head-$(CONFIG_M68360) := arch/m68k/platform/68360/head.o -head-$(CONFIG_M68000) := arch/m68k/platform/68000/head.o -head-$(CONFIG_COLDFIRE) := arch/m68k/platform/coldfire/head.o +head-$(CONFIG_M68360) := arch/m68k/68360/head.o +head-$(CONFIG_M68000) := arch/m68k/68000/head.o +head-$(CONFIG_COLDFIRE) := arch/m68k/coldfire/head.o core-y += arch/m68k/kernel/ arch/m68k/mm/ libs-y += arch/m68k/lib/ @@ -114,9 +114,9 @@ core-$(CONFIG_NATFEAT) += arch/m68k/emu/ core-$(CONFIG_M68040) += arch/m68k/fpsp040/ core-$(CONFIG_M68060) += arch/m68k/ifpsp060/ core-$(CONFIG_M68KFPU_EMU) += arch/m68k/math-emu/ -core-$(CONFIG_M68360) += arch/m68k/platform/68360/ -core-$(CONFIG_M68000) += arch/m68k/platform/68000/ -core-$(CONFIG_COLDFIRE) += arch/m68k/platform/coldfire/ +core-$(CONFIG_M68360) += arch/m68k/68360/ +core-$(CONFIG_M68000) += arch/m68k/68000/ +core-$(CONFIG_COLDFIRE) += arch/m68k/coldfire/ all: zImage diff --git a/arch/m68k/platform/coldfire/Makefile b/arch/m68k/coldfire/Makefile similarity index 100% rename from arch/m68k/platform/coldfire/Makefile rename to arch/m68k/coldfire/Makefile diff --git a/arch/m68k/platform/coldfire/cache.c b/arch/m68k/coldfire/cache.c similarity index 100% rename from arch/m68k/platform/coldfire/cache.c rename to arch/m68k/coldfire/cache.c diff --git a/arch/m68k/platform/coldfire/clk.c b/arch/m68k/coldfire/clk.c similarity index 100% rename from arch/m68k/platform/coldfire/clk.c rename to arch/m68k/coldfire/clk.c diff --git a/arch/m68k/platform/coldfire/device.c b/arch/m68k/coldfire/device.c similarity index 100% rename from arch/m68k/platform/coldfire/device.c rename to arch/m68k/coldfire/device.c diff --git a/arch/m68k/platform/coldfire/dma.c b/arch/m68k/coldfire/dma.c similarity index 100% rename from arch/m68k/platform/coldfire/dma.c rename to arch/m68k/coldfire/dma.c diff --git a/arch/m68k/platform/coldfire/dma_timer.c b/arch/m68k/coldfire/dma_timer.c similarity index 100% rename from arch/m68k/platform/coldfire/dma_timer.c rename to arch/m68k/coldfire/dma_timer.c diff --git a/arch/m68k/platform/coldfire/entry.S b/arch/m68k/coldfire/entry.S similarity index 98% rename from arch/m68k/platform/coldfire/entry.S rename to arch/m68k/coldfire/entry.S index 881ab8e379d4c68a42854b372fb906ef8f32d07c..52d312d5b4d4f68337aa8f9b711f50b1a1ad9eda 100644 --- a/arch/m68k/platform/coldfire/entry.S +++ b/arch/m68k/coldfire/entry.S @@ -1,5 +1,5 @@ /* - * linux/arch/m68knommu/platform/5307/entry.S + * entry.S -- interrupt and exception processing for ColdFire * * Copyright (C) 1999-2007, Greg Ungerer (gerg@snapgear.com) * Copyright (C) 1998 D. Jeff Dionne , diff --git a/arch/m68k/platform/coldfire/firebee.c b/arch/m68k/coldfire/firebee.c similarity index 100% rename from arch/m68k/platform/coldfire/firebee.c rename to arch/m68k/coldfire/firebee.c diff --git a/arch/m68k/platform/coldfire/gpio.c b/arch/m68k/coldfire/gpio.c similarity index 100% rename from arch/m68k/platform/coldfire/gpio.c rename to arch/m68k/coldfire/gpio.c diff --git a/arch/m68k/platform/coldfire/head.S b/arch/m68k/coldfire/head.S similarity index 100% rename from arch/m68k/platform/coldfire/head.S rename to arch/m68k/coldfire/head.S diff --git a/arch/m68k/platform/coldfire/intc-2.c b/arch/m68k/coldfire/intc-2.c similarity index 100% rename from arch/m68k/platform/coldfire/intc-2.c rename to arch/m68k/coldfire/intc-2.c diff --git a/arch/m68k/platform/coldfire/intc-5249.c b/arch/m68k/coldfire/intc-5249.c similarity index 100% rename from arch/m68k/platform/coldfire/intc-5249.c rename to arch/m68k/coldfire/intc-5249.c diff --git a/arch/m68k/platform/coldfire/intc-525x.c b/arch/m68k/coldfire/intc-525x.c similarity index 100% rename from arch/m68k/platform/coldfire/intc-525x.c rename to arch/m68k/coldfire/intc-525x.c diff --git a/arch/m68k/platform/coldfire/intc-5272.c b/arch/m68k/coldfire/intc-5272.c similarity index 99% rename from arch/m68k/platform/coldfire/intc-5272.c rename to arch/m68k/coldfire/intc-5272.c index d7b695629a7e3e2014a38189bf71d7bc403bcc1e..d1e2fbad327cecaa4c785e4b270177a6c030cbb5 100644 --- a/arch/m68k/platform/coldfire/intc-5272.c +++ b/arch/m68k/coldfire/intc-5272.c @@ -36,7 +36,7 @@ * they also need acknowledging via acknowledge bits. */ struct irqmap { - unsigned char icr; + unsigned int icr; unsigned char index; unsigned char ack; }; diff --git a/arch/m68k/platform/coldfire/intc-simr.c b/arch/m68k/coldfire/intc-simr.c similarity index 100% rename from arch/m68k/platform/coldfire/intc-simr.c rename to arch/m68k/coldfire/intc-simr.c diff --git a/arch/m68k/platform/coldfire/intc.c b/arch/m68k/coldfire/intc.c similarity index 100% rename from arch/m68k/platform/coldfire/intc.c rename to arch/m68k/coldfire/intc.c diff --git a/arch/m68k/platform/coldfire/m5206.c b/arch/m68k/coldfire/m5206.c similarity index 96% rename from arch/m68k/platform/coldfire/m5206.c rename to arch/m68k/coldfire/m5206.c index 0e55f449a88c445f47d51e289599ad76349e5648..8945f5e7b39c6dc05224beca10135440c684e1a4 100644 --- a/arch/m68k/platform/coldfire/m5206.c +++ b/arch/m68k/coldfire/m5206.c @@ -1,7 +1,7 @@ /***************************************************************************/ /* - * linux/arch/m68knommu/platform/5206/config.c + * m5206.c -- platform support for ColdFire 5206 based boards * * Copyright (C) 1999-2002, Greg Ungerer (gerg@snapgear.com) * Copyright (C) 2000-2001, Lineo Inc. (www.lineo.com) diff --git a/arch/m68k/platform/coldfire/m520x.c b/arch/m68k/coldfire/m520x.c similarity index 98% rename from arch/m68k/platform/coldfire/m520x.c rename to arch/m68k/coldfire/m520x.c index 4040a3c93733272465a015b1a1fff422983972f7..173834f251ebe96ea6ab9316bd31ce4054a476a6 100644 --- a/arch/m68k/platform/coldfire/m520x.c +++ b/arch/m68k/coldfire/m520x.c @@ -1,7 +1,7 @@ /***************************************************************************/ /* - * linux/arch/m68knommu/platform/520x/config.c + * m520x.c -- platform support for ColdFire 520x based boards * * Copyright (C) 2005, Freescale (www.freescale.com) * Copyright (C) 2005, Intec Automation (mike@steroidmicros.com) diff --git a/arch/m68k/platform/coldfire/m523x.c b/arch/m68k/coldfire/m523x.c similarity index 97% rename from arch/m68k/platform/coldfire/m523x.c rename to arch/m68k/coldfire/m523x.c index 6b7135e6d5b42ffa08411becd33b7c451ce305aa..a191a467eff276a3f2a4a8027592f0c2e09fc9c3 100644 --- a/arch/m68k/platform/coldfire/m523x.c +++ b/arch/m68k/coldfire/m523x.c @@ -1,7 +1,7 @@ /***************************************************************************/ /* - * linux/arch/m68knommu/platform/523x/config.c + * m523x.c -- platform support for ColdFire 523x based boards * * Sub-architcture dependent initialization code for the Freescale * 523x CPUs. diff --git a/arch/m68k/platform/coldfire/m5249.c b/arch/m68k/coldfire/m5249.c similarity index 97% rename from arch/m68k/platform/coldfire/m5249.c rename to arch/m68k/coldfire/m5249.c index f6253a3313b3861681308c41f3a069c882ceb7f8..e48f55adc44777bcd6b7c6df456173064e7395b8 100644 --- a/arch/m68k/platform/coldfire/m5249.c +++ b/arch/m68k/coldfire/m5249.c @@ -1,7 +1,7 @@ /***************************************************************************/ /* - * linux/arch/m68knommu/platform/5249/config.c + * m5249.c -- platform support for ColdFire 5249 based boards * * Copyright (C) 2002, Greg Ungerer (gerg@snapgear.com) */ diff --git a/arch/m68k/platform/coldfire/m525x.c b/arch/m68k/coldfire/m525x.c similarity index 97% rename from arch/m68k/platform/coldfire/m525x.c rename to arch/m68k/coldfire/m525x.c index 1adba3909035170b6f57be9a7d5b4589ae83f099..3d8583e2187c5bf135685cf5a325852e721823e1 100644 --- a/arch/m68k/platform/coldfire/m525x.c +++ b/arch/m68k/coldfire/m525x.c @@ -1,7 +1,7 @@ /***************************************************************************/ /* - * 525x.c + * 525x.c -- platform support for ColdFire 525x based boards * * Copyright (C) 2012, Steven King */ diff --git a/arch/m68k/platform/coldfire/m5272.c b/arch/m68k/coldfire/m5272.c similarity index 98% rename from arch/m68k/platform/coldfire/m5272.c rename to arch/m68k/coldfire/m5272.c index 8a4d3cc322c60126a5f92b9fa337e58ba38f45fe..b15219ed22bf5d61ab34392040826fc6823cab6e 100644 --- a/arch/m68k/platform/coldfire/m5272.c +++ b/arch/m68k/coldfire/m5272.c @@ -1,7 +1,7 @@ /***************************************************************************/ /* - * linux/arch/m68knommu/platform/5272/config.c + * m5272.c -- platform support for ColdFire 5272 based boards * * Copyright (C) 1999-2002, Greg Ungerer (gerg@snapgear.com) * Copyright (C) 2001-2002, SnapGear Inc. (www.snapgear.com) diff --git a/arch/m68k/platform/coldfire/m527x.c b/arch/m68k/coldfire/m527x.c similarity index 97% rename from arch/m68k/platform/coldfire/m527x.c rename to arch/m68k/coldfire/m527x.c index 62d81ef016f1742081b35d2ce534a9b2eabcfa09..2ba470735bed0bc20486adaba2546853b0d9c344 100644 --- a/arch/m68k/platform/coldfire/m527x.c +++ b/arch/m68k/coldfire/m527x.c @@ -1,10 +1,10 @@ /***************************************************************************/ /* - * linux/arch/m68knommu/platform/527x/config.c + * m527x.c -- platform support for ColdFire 527x based boards * * Sub-architcture dependent initialization code for the Freescale - * 5270/5271 CPUs. + * 5270/5271 and 5274/5275 CPUs. * * Copyright (C) 1999-2004, Greg Ungerer (gerg@snapgear.com) * Copyright (C) 2001-2004, SnapGear Inc. (www.snapgear.com) diff --git a/arch/m68k/platform/coldfire/m528x.c b/arch/m68k/coldfire/m528x.c similarity index 98% rename from arch/m68k/platform/coldfire/m528x.c rename to arch/m68k/coldfire/m528x.c index 21cd161d36f19d570a693bb6d315d56846416f6d..45e947aeade4a3f0e01807c9b4fb4d5ac64dfe75 100644 --- a/arch/m68k/platform/coldfire/m528x.c +++ b/arch/m68k/coldfire/m528x.c @@ -1,7 +1,7 @@ /***************************************************************************/ /* - * linux/arch/m68knommu/platform/528x/config.c + * m528x.c -- platform support for ColdFire 528x based boards * * Sub-architcture dependent initialization code for the Freescale * 5280, 5281 and 5282 CPUs. diff --git a/arch/m68k/platform/coldfire/m5307.c b/arch/m68k/coldfire/m5307.c similarity index 97% rename from arch/m68k/platform/coldfire/m5307.c rename to arch/m68k/coldfire/m5307.c index 887435361386fb7e4cd5d831de2816bcd0948e45..2da1d146e344aee94a712eb624efd40c5cc1952c 100644 --- a/arch/m68k/platform/coldfire/m5307.c +++ b/arch/m68k/coldfire/m5307.c @@ -1,7 +1,7 @@ /***************************************************************************/ /* - * linux/arch/m68knommu/platform/5307/config.c + * m5307.c -- platform support for ColdFire 5307 based boards * * Copyright (C) 1999-2002, Greg Ungerer (gerg@snapgear.com) * Copyright (C) 2000, Lineo (www.lineo.com) diff --git a/arch/m68k/platform/coldfire/m53xx.c b/arch/m68k/coldfire/m53xx.c similarity index 100% rename from arch/m68k/platform/coldfire/m53xx.c rename to arch/m68k/coldfire/m53xx.c diff --git a/arch/m68k/platform/coldfire/m5407.c b/arch/m68k/coldfire/m5407.c similarity index 95% rename from arch/m68k/platform/coldfire/m5407.c rename to arch/m68k/coldfire/m5407.c index 2fb3cdbfde30164e11bf7aa5341b48309681621f..738eba6be40ecadb0f0238b752c00d9415c8228a 100644 --- a/arch/m68k/platform/coldfire/m5407.c +++ b/arch/m68k/coldfire/m5407.c @@ -1,7 +1,7 @@ /***************************************************************************/ /* - * linux/arch/m68knommu/platform/5407/config.c + * m5407.c -- platform support for ColdFire 5407 based boards * * Copyright (C) 1999-2002, Greg Ungerer (gerg@snapgear.com) * Copyright (C) 2000, Lineo (www.lineo.com) diff --git a/arch/m68k/platform/coldfire/m5441x.c b/arch/m68k/coldfire/m5441x.c similarity index 100% rename from arch/m68k/platform/coldfire/m5441x.c rename to arch/m68k/coldfire/m5441x.c diff --git a/arch/m68k/platform/coldfire/m54xx.c b/arch/m68k/coldfire/m54xx.c similarity index 97% rename from arch/m68k/platform/coldfire/m54xx.c rename to arch/m68k/coldfire/m54xx.c index 952da53aa0bcd166f1c6fc7c972bb12b46c5f39b..075aaabd136026a408ee4560ee4870127c6b1b9d 100644 --- a/arch/m68k/platform/coldfire/m54xx.c +++ b/arch/m68k/coldfire/m54xx.c @@ -1,7 +1,7 @@ /***************************************************************************/ /* - * linux/arch/m68knommu/platform/54xx/config.c + * m54xx.c -- platform support for ColdFire 54xx based boards * * Copyright (C) 2010, Philippe De Muyter */ @@ -23,7 +23,6 @@ #include #include #include -#include #ifdef CONFIG_MMU #include #endif diff --git a/arch/m68k/platform/coldfire/mcf8390.c b/arch/m68k/coldfire/mcf8390.c similarity index 100% rename from arch/m68k/platform/coldfire/mcf8390.c rename to arch/m68k/coldfire/mcf8390.c diff --git a/arch/m68k/platform/coldfire/nettel.c b/arch/m68k/coldfire/nettel.c similarity index 100% rename from arch/m68k/platform/coldfire/nettel.c rename to arch/m68k/coldfire/nettel.c diff --git a/arch/m68k/platform/coldfire/pci.c b/arch/m68k/coldfire/pci.c similarity index 100% rename from arch/m68k/platform/coldfire/pci.c rename to arch/m68k/coldfire/pci.c diff --git a/arch/m68k/platform/coldfire/pit.c b/arch/m68k/coldfire/pit.c similarity index 100% rename from arch/m68k/platform/coldfire/pit.c rename to arch/m68k/coldfire/pit.c diff --git a/arch/m68k/platform/coldfire/reset.c b/arch/m68k/coldfire/reset.c similarity index 100% rename from arch/m68k/platform/coldfire/reset.c rename to arch/m68k/coldfire/reset.c diff --git a/arch/m68k/platform/coldfire/sltimers.c b/arch/m68k/coldfire/sltimers.c similarity index 100% rename from arch/m68k/platform/coldfire/sltimers.c rename to arch/m68k/coldfire/sltimers.c diff --git a/arch/m68k/platform/coldfire/timers.c b/arch/m68k/coldfire/timers.c similarity index 100% rename from arch/m68k/platform/coldfire/timers.c rename to arch/m68k/coldfire/timers.c diff --git a/arch/m68k/platform/coldfire/vectors.c b/arch/m68k/coldfire/vectors.c similarity index 96% rename from arch/m68k/platform/coldfire/vectors.c rename to arch/m68k/coldfire/vectors.c index a4dbdecbec7a0a6266e664839aeb3b118720a1e1..08923fe600e013d95610376959016e77064f3f91 100644 --- a/arch/m68k/platform/coldfire/vectors.c +++ b/arch/m68k/coldfire/vectors.c @@ -1,7 +1,7 @@ /***************************************************************************/ /* - * linux/arch/m68knommu/platform/coldfire/vectors.c + * vectors.c -- high level trap setup for ColdFire * * Copyright (C) 1999-2007, Greg Ungerer */ diff --git a/arch/m68k/include/asm/io_no.h b/arch/m68k/include/asm/io_no.h index 52f7e8499172e101b5ba3d925c9d9d9694c325c1..be4b5a813ad401972624886802f5f69aa047e7dc 100644 --- a/arch/m68k/include/asm/io_no.h +++ b/arch/m68k/include/asm/io_no.h @@ -179,6 +179,15 @@ static inline void *ioremap_fullcache(unsigned long physaddr, unsigned long size */ #define xlate_dev_kmem_ptr(p) p +static inline void __iomem *ioport_map(unsigned long port, unsigned int nr) +{ + return (void __iomem *) port; +} + +static inline void ioport_unmap(void __iomem *p) +{ +} + #endif /* __KERNEL__ */ #endif /* _M68KNOMMU_IO_H */ diff --git a/arch/m68k/include/asm/m54xxpci.h b/arch/m68k/include/asm/m54xxpci.h index 6fbf54f72f2e0a5cc27b7b29207e68e2d702826c..4687f5aa374170c5780fb1f817e08770654f303d 100644 --- a/arch/m68k/include/asm/m54xxpci.h +++ b/arch/m68k/include/asm/m54xxpci.h @@ -72,7 +72,7 @@ #define PCIRFWPR (CONFIG_MBAR + 0x84d4) /* RX FIFO write pointer */ #define PACR (CONFIG_MBAR + 0xc00) /* PCI arbiter control */ -#define PASR (COFNIG_MBAR + 0xc04) /* PCI arbiter status */ +#define PASR (CONFIG_MBAR + 0xc04) /* PCI arbiter status */ /* * Definitions for the Global status and control register. diff --git a/arch/m68k/platform/Makefile b/arch/m68k/platform/Makefile deleted file mode 100644 index fc932bf65d349236fb479d9a88e1fc11c0989601..0000000000000000000000000000000000000000 --- a/arch/m68k/platform/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# -# Makefile for the arch/m68knommu/platform. -# diff --git a/arch/metag/kernel/perf/perf_event.c b/arch/metag/kernel/perf/perf_event.c index 02c08737f6aa0579c03b1d237d2db240efb509c1..2478ec6d23c9e3e0b14954d670f9f96e925c8f6d 100644 --- a/arch/metag/kernel/perf/perf_event.c +++ b/arch/metag/kernel/perf/perf_event.c @@ -258,7 +258,7 @@ int metag_pmu_event_set_period(struct perf_event *event, static void metag_pmu_start(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc = &event->hw; int idx = hwc->idx; @@ -306,7 +306,7 @@ static void metag_pmu_stop(struct perf_event *event, int flags) static int metag_pmu_add(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc = &event->hw; int idx = 0, ret = 0; @@ -348,7 +348,7 @@ static int metag_pmu_add(struct perf_event *event, int flags) static void metag_pmu_del(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc = &event->hw; int idx = hwc->idx; @@ -597,7 +597,7 @@ static int _hw_perf_event_init(struct perf_event *event) static void metag_pmu_enable_counter(struct hw_perf_event *event, int idx) { - struct cpu_hw_events *events = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *events = this_cpu_ptr(&cpu_hw_events); unsigned int config = event->config; unsigned int tmp = config & 0xf0; unsigned long flags; @@ -670,7 +670,7 @@ static void metag_pmu_enable_counter(struct hw_perf_event *event, int idx) static void metag_pmu_disable_counter(struct hw_perf_event *event, int idx) { - struct cpu_hw_events *events = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *events = this_cpu_ptr(&cpu_hw_events); unsigned int tmp = 0; unsigned long flags; @@ -718,7 +718,7 @@ static u64 metag_pmu_read_counter(int idx) static void metag_pmu_write_counter(int idx, u32 val) { - struct cpu_hw_events *events = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *events = this_cpu_ptr(&cpu_hw_events); u32 tmp = 0; unsigned long flags; @@ -751,7 +751,7 @@ static int metag_pmu_event_map(int idx) static irqreturn_t metag_pmu_counter_overflow(int irq, void *dev) { int idx = (int)dev; - struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events); struct perf_event *event = cpuhw->events[idx]; struct hw_perf_event *hwc = &event->hw; struct pt_regs *regs = get_irq_regs(); diff --git a/arch/microblaze/include/asm/syscall.h b/arch/microblaze/include/asm/syscall.h index 9bc431783105caf678b92a08b64e8daa5a2964d1..53cfaf34c34330eeda9811a96b86867bd924cd45 100644 --- a/arch/microblaze/include/asm/syscall.h +++ b/arch/microblaze/include/asm/syscall.h @@ -1,6 +1,7 @@ #ifndef __ASM_MICROBLAZE_SYSCALL_H #define __ASM_MICROBLAZE_SYSCALL_H +#include #include #include #include @@ -99,4 +100,8 @@ static inline void syscall_set_arguments(struct task_struct *task, asmlinkage long do_syscall_trace_enter(struct pt_regs *regs); asmlinkage void do_syscall_trace_leave(struct pt_regs *regs); +static inline int syscall_get_arch(void) +{ + return AUDIT_ARCH_MICROBLAZE; +} #endif /* __ASM_MICROBLAZE_SYSCALL_H */ diff --git a/arch/microblaze/kernel/ptrace.c b/arch/microblaze/kernel/ptrace.c index 39cf50841f6d57f5e6253cd9e8a04086604bc159..bb10637ce688b389b61a4516f2fb691f0d7b4266 100644 --- a/arch/microblaze/kernel/ptrace.c +++ b/arch/microblaze/kernel/ptrace.c @@ -147,8 +147,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) */ ret = -1L; - audit_syscall_entry(EM_MICROBLAZE, regs->r12, regs->r5, regs->r6, - regs->r7, regs->r8); + audit_syscall_entry(regs->r12, regs->r5, regs->r6, regs->r7, regs->r8); return ret ?: regs->r12; } diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 574c430006997427b564ef9bdeda2c16c3497279..ad6badb6be715735715e25ad3198b19e9f54191b 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -29,6 +29,7 @@ config MIPS select GENERIC_ATOMIC64 if !64BIT select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE select HAVE_DMA_ATTRS + select HAVE_DMA_CONTIGUOUS select HAVE_DMA_API_DEBUG select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW @@ -353,6 +354,7 @@ config MIPS_SEAD3 bool "MIPS SEAD3 board" select BOOT_ELF32 select BOOT_RAW + select BUILTIN_DTB select CEVT_R4K select CSRC_R4K select CSRC_GIC @@ -742,6 +744,7 @@ config CAVIUM_OCTEON_SOC select ARCH_SPARSEMEM_ENABLE select SYS_SUPPORTS_SMP select NR_CPUS_DEFAULT_16 + select BUILTIN_DTB help This option supports all of the Octeon reference boards from Cavium Networks. It builds a kernel that dynamically determines the Octeon @@ -2482,6 +2485,9 @@ config USE_OF select OF_EARLY_FLATTREE select IRQ_DOMAIN +config BUILTIN_DTB + bool + endmenu config LOCKDEP_SUPPORT diff --git a/arch/mips/Makefile b/arch/mips/Makefile index bbac51e11179af67698250417512a92136a11a0f..23cb94806fbc5d0f370110cc0da8936f54b80719 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -333,6 +333,16 @@ endif CLEAN_FILES += vmlinux.32 vmlinux.64 +# device-trees +core-$(CONFIG_BUILTIN_DTB) += arch/mips/boot/dts/ + +%.dtb %.dtb.S %.dtb.o: | scripts + $(Q)$(MAKE) $(build)=arch/mips/boot/dts arch/mips/boot/dts/$@ + +PHONY += dtbs +dtbs: scripts + $(Q)$(MAKE) $(build)=arch/mips/boot/dts dtbs + archprepare: ifdef CONFIG_MIPS32_N32 @echo ' Checking missing-syscalls for N32' @@ -367,6 +377,7 @@ define archhelp echo ' vmlinuz.srec - SREC zboot image' echo ' uImage - U-Boot image' echo ' uImage.gz - U-Boot image (gzip)' + echo ' dtbs - Device-tree blobs for enabled boards' echo echo ' These will be default as appropriate for a configured platform.' endef diff --git a/arch/mips/alchemy/devboards/db1300.c b/arch/mips/alchemy/devboards/db1300.c index ef93ee3f6a2c03ad6f152edbdb201c8b7a36bc30..1c64fdbe4c813c41f1a4bdb68150336e428bfb21 100644 --- a/arch/mips/alchemy/devboards/db1300.c +++ b/arch/mips/alchemy/devboards/db1300.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -711,6 +712,46 @@ static struct platform_device db1300_lcd_dev = { /**********************************************************************/ +static void db1300_wm97xx_irqen(struct wm97xx *wm, int enable) +{ + if (enable) + enable_irq(DB1300_AC97_PEN_INT); + else + disable_irq_nosync(DB1300_AC97_PEN_INT); +} + +static struct wm97xx_mach_ops db1300_wm97xx_ops = { + .irq_enable = db1300_wm97xx_irqen, + .irq_gpio = WM97XX_GPIO_3, +}; + +static int db1300_wm97xx_probe(struct platform_device *pdev) +{ + struct wm97xx *wm = platform_get_drvdata(pdev); + + /* external pendown indicator */ + wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN, + WM97XX_GPIO_POL_LOW, WM97XX_GPIO_STICKY, + WM97XX_GPIO_WAKE); + + /* internal "virtual" pendown gpio */ + wm97xx_config_gpio(wm, WM97XX_GPIO_3, WM97XX_GPIO_OUT, + WM97XX_GPIO_POL_LOW, WM97XX_GPIO_NOTSTICKY, + WM97XX_GPIO_NOWAKE); + + wm->pen_irq = DB1300_AC97_PEN_INT; + + return wm97xx_register_mach_ops(wm, &db1300_wm97xx_ops); +} + +static struct platform_driver db1300_wm97xx_driver = { + .driver.name = "wm97xx-touch", + .driver.owner = THIS_MODULE, + .probe = db1300_wm97xx_probe, +}; + +/**********************************************************************/ + static struct platform_device *db1300_dev[] __initdata = { &db1300_eth_dev, &db1300_i2c_dev, @@ -755,6 +796,9 @@ int __init db1300_dev_setup(void) i2c_register_board_info(0, db1300_i2c_devs, ARRAY_SIZE(db1300_i2c_devs)); + if (platform_driver_register(&db1300_wm97xx_driver)) + pr_warn("DB1300: failed to init touch pen irq support!\n"); + /* Audio PSC clock is supplied by codecs (PSC1, 2) */ __raw_writel(PSC_SEL_CLK_SERCLK, (void __iomem *)KSEG1ADDR(AU1300_PSC1_PHYS_ADDR) + PSC_SEL_OFFSET); @@ -762,9 +806,10 @@ int __init db1300_dev_setup(void) __raw_writel(PSC_SEL_CLK_SERCLK, (void __iomem *)KSEG1ADDR(AU1300_PSC2_PHYS_ADDR) + PSC_SEL_OFFSET); wmb(); - /* I2C uses internal 48MHz EXTCLK1 */ + /* I2C driver wants 50MHz, get as close as possible */ c = clk_get(NULL, "psc3_intclk"); if (!IS_ERR(c)) { + clk_set_rate(c, 50000000); clk_prepare_enable(c); clk_put(c); } diff --git a/arch/mips/alchemy/devboards/db1550.c b/arch/mips/alchemy/devboards/db1550.c index 7e89936f763e9a9c4e5a4c6572a416e6dcd8c547..0fd5177e35ab27bf2fa86dcbdf7e20c58073ddf0 100644 --- a/arch/mips/alchemy/devboards/db1550.c +++ b/arch/mips/alchemy/devboards/db1550.c @@ -34,12 +34,9 @@ static void __init db1550_hw_setup(void) void __iomem *base; unsigned long v; - /* complete SPI setup: link psc0_intclk to a 48MHz source, - * and assign GPIO16 to PSC0_SYNC1 (SPI cs# line) as well as PSC1_SYNC - * for AC97 on PB1550. + /* complete pin setup: assign GPIO16 to PSC0_SYNC1 (SPI cs# line) + * as well as PSC1_SYNC for AC97 on PB1550. */ - v = alchemy_rdsys(AU1000_SYS_CLKSRC); - alchemy_wrsys(v | 0x000001e0, AU1000_SYS_CLKSRC); v = alchemy_rdsys(AU1000_SYS_PINFUNC); alchemy_wrsys(v | 1 | SYS_PF_PSC1_S1, AU1000_SYS_PINFUNC); @@ -586,11 +583,13 @@ int __init db1550_dev_setup(void) c = clk_get(NULL, "psc0_intclk"); if (!IS_ERR(c)) { + clk_set_rate(c, 50000000); clk_prepare_enable(c); clk_put(c); } c = clk_get(NULL, "psc2_intclk"); if (!IS_ERR(c)) { + clk_set_rate(c, db1550_spi_platdata.mainclk_hz); clk_prepare_enable(c); clk_put(c); } diff --git a/arch/mips/alchemy/devboards/platform.c b/arch/mips/alchemy/devboards/platform.c index 8df86eb94972e06c14971b21f565d241c43313d5..be139a0198b0bf40d3488dd7926f16cd58106afc 100644 --- a/arch/mips/alchemy/devboards/platform.c +++ b/arch/mips/alchemy/devboards/platform.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -53,6 +54,8 @@ static void db1x_power_off(void) { bcsr_write(BCSR_RESETS, 0); bcsr_write(BCSR_SYSTEM, BCSR_SYSTEM_PWROFF | BCSR_SYSTEM_RESET); + while (1) /* sit and spin */ + cpu_wait(); } static void db1x_reset(char *c) diff --git a/arch/mips/boot/.gitignore b/arch/mips/boot/.gitignore index a73d6e2c4f64fe55e033c05e0ec4aa1eed8eed4e..d3962cd5ce0c129024666704ecfacdacb58a7cc9 100644 --- a/arch/mips/boot/.gitignore +++ b/arch/mips/boot/.gitignore @@ -5,3 +5,4 @@ zImage zImage.tmp calc_vmlinuz_load_addr uImage +*.dtb diff --git a/arch/mips/boot/dts/Makefile b/arch/mips/boot/dts/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..ca9c90e2cabf0cef63ed5e7025e6e42f52552167 --- /dev/null +++ b/arch/mips/boot/dts/Makefile @@ -0,0 +1,20 @@ +dtb-$(CONFIG_CAVIUM_OCTEON_SOC) += octeon_3xxx.dtb octeon_68xx.dtb +dtb-$(CONFIG_DT_EASY50712) += easy50712.dtb +dtb-$(CONFIG_DT_XLP_EVP) += xlp_evp.dtb +dtb-$(CONFIG_DT_XLP_SVP) += xlp_svp.dtb +dtb-$(CONFIG_DT_XLP_FVP) += xlp_fvp.dtb +dtb-$(CONFIG_DT_XLP_GVP) += xlp_gvp.dtb +dtb-$(CONFIG_DTB_RT2880_EVAL) += rt2880_eval.dtb +dtb-$(CONFIG_DTB_RT305X_EVAL) += rt3052_eval.dtb +dtb-$(CONFIG_DTB_RT3883_EVAL) += rt3883_eval.dtb +dtb-$(CONFIG_DTB_MT7620A_EVAL) += mt7620a_eval.dtb +dtb-$(CONFIG_MIPS_SEAD3) += sead3.dtb + +obj-y += $(patsubst %.dtb, %.dtb.o, $(dtb-y)) + +targets += dtbs +targets += $(dtb-y) + +dtbs: $(addprefix $(obj)/, $(dtb-y)) + +clean-files += *.dtb *.dtb.S diff --git a/arch/mips/lantiq/dts/danube.dtsi b/arch/mips/boot/dts/danube.dtsi similarity index 100% rename from arch/mips/lantiq/dts/danube.dtsi rename to arch/mips/boot/dts/danube.dtsi diff --git a/arch/mips/lantiq/dts/easy50712.dts b/arch/mips/boot/dts/easy50712.dts similarity index 100% rename from arch/mips/lantiq/dts/easy50712.dts rename to arch/mips/boot/dts/easy50712.dts diff --git a/arch/mips/ralink/dts/mt7620a.dtsi b/arch/mips/boot/dts/mt7620a.dtsi similarity index 100% rename from arch/mips/ralink/dts/mt7620a.dtsi rename to arch/mips/boot/dts/mt7620a.dtsi diff --git a/arch/mips/ralink/dts/mt7620a_eval.dts b/arch/mips/boot/dts/mt7620a_eval.dts similarity index 100% rename from arch/mips/ralink/dts/mt7620a_eval.dts rename to arch/mips/boot/dts/mt7620a_eval.dts diff --git a/arch/mips/cavium-octeon/octeon_3xxx.dts b/arch/mips/boot/dts/octeon_3xxx.dts similarity index 100% rename from arch/mips/cavium-octeon/octeon_3xxx.dts rename to arch/mips/boot/dts/octeon_3xxx.dts diff --git a/arch/mips/cavium-octeon/octeon_68xx.dts b/arch/mips/boot/dts/octeon_68xx.dts similarity index 100% rename from arch/mips/cavium-octeon/octeon_68xx.dts rename to arch/mips/boot/dts/octeon_68xx.dts diff --git a/arch/mips/ralink/dts/rt2880.dtsi b/arch/mips/boot/dts/rt2880.dtsi similarity index 100% rename from arch/mips/ralink/dts/rt2880.dtsi rename to arch/mips/boot/dts/rt2880.dtsi diff --git a/arch/mips/ralink/dts/rt2880_eval.dts b/arch/mips/boot/dts/rt2880_eval.dts similarity index 100% rename from arch/mips/ralink/dts/rt2880_eval.dts rename to arch/mips/boot/dts/rt2880_eval.dts diff --git a/arch/mips/ralink/dts/rt3050.dtsi b/arch/mips/boot/dts/rt3050.dtsi similarity index 100% rename from arch/mips/ralink/dts/rt3050.dtsi rename to arch/mips/boot/dts/rt3050.dtsi diff --git a/arch/mips/ralink/dts/rt3052_eval.dts b/arch/mips/boot/dts/rt3052_eval.dts similarity index 100% rename from arch/mips/ralink/dts/rt3052_eval.dts rename to arch/mips/boot/dts/rt3052_eval.dts diff --git a/arch/mips/ralink/dts/rt3883.dtsi b/arch/mips/boot/dts/rt3883.dtsi similarity index 100% rename from arch/mips/ralink/dts/rt3883.dtsi rename to arch/mips/boot/dts/rt3883.dtsi diff --git a/arch/mips/ralink/dts/rt3883_eval.dts b/arch/mips/boot/dts/rt3883_eval.dts similarity index 100% rename from arch/mips/ralink/dts/rt3883_eval.dts rename to arch/mips/boot/dts/rt3883_eval.dts diff --git a/arch/mips/mti-sead3/sead3.dts b/arch/mips/boot/dts/sead3.dts similarity index 100% rename from arch/mips/mti-sead3/sead3.dts rename to arch/mips/boot/dts/sead3.dts diff --git a/arch/mips/netlogic/dts/xlp_evp.dts b/arch/mips/boot/dts/xlp_evp.dts similarity index 100% rename from arch/mips/netlogic/dts/xlp_evp.dts rename to arch/mips/boot/dts/xlp_evp.dts diff --git a/arch/mips/netlogic/dts/xlp_fvp.dts b/arch/mips/boot/dts/xlp_fvp.dts similarity index 100% rename from arch/mips/netlogic/dts/xlp_fvp.dts rename to arch/mips/boot/dts/xlp_fvp.dts diff --git a/arch/mips/netlogic/dts/xlp_gvp.dts b/arch/mips/boot/dts/xlp_gvp.dts similarity index 100% rename from arch/mips/netlogic/dts/xlp_gvp.dts rename to arch/mips/boot/dts/xlp_gvp.dts diff --git a/arch/mips/netlogic/dts/xlp_svp.dts b/arch/mips/boot/dts/xlp_svp.dts similarity index 100% rename from arch/mips/netlogic/dts/xlp_svp.dts rename to arch/mips/boot/dts/xlp_svp.dts diff --git a/arch/mips/cavium-octeon/.gitignore b/arch/mips/cavium-octeon/.gitignore deleted file mode 100644 index 39c968605ff63a3b451fa7ab9e86bc08030329e3..0000000000000000000000000000000000000000 --- a/arch/mips/cavium-octeon/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.dtb.S -*.dtb diff --git a/arch/mips/cavium-octeon/Makefile b/arch/mips/cavium-octeon/Makefile index 4e952043c922177590a01540af71304ae51dc120..42f5f1a4b40a4c985169e96815017483460f5b34 100644 --- a/arch/mips/cavium-octeon/Makefile +++ b/arch/mips/cavium-octeon/Makefile @@ -20,13 +20,3 @@ obj-y += executive/ obj-$(CONFIG_MTD) += flash_setup.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_OCTEON_ILM) += oct_ilm.o - -DTS_FILES = octeon_3xxx.dts octeon_68xx.dts -DTB_FILES = $(patsubst %.dts, %.dtb, $(DTS_FILES)) - -obj-y += $(patsubst %.dts, %.dtb.o, $(DTS_FILES)) - -# Let's keep the .dtb files around in case we want to look at them. -.SECONDARY: $(addprefix $(obj)/, $(DTB_FILES)) - -clean-files += $(DTB_FILES) $(patsubst %.dtb, %.dtb.S, $(DTB_FILES)) diff --git a/arch/mips/cavium-octeon/octeon-irq.c b/arch/mips/cavium-octeon/octeon-irq.c index 1b82ac6921e031644687979d69b4287064751891..7417340496756d30d159aae9b48e4af0f8edf531 100644 --- a/arch/mips/cavium-octeon/octeon-irq.c +++ b/arch/mips/cavium-octeon/octeon-irq.c @@ -264,13 +264,13 @@ static void octeon_irq_ciu_enable_local(struct irq_data *data) unsigned long *pen; unsigned long flags; union octeon_ciu_chip_data cd; - raw_spinlock_t *lock = &__get_cpu_var(octeon_irq_ciu_spinlock); + raw_spinlock_t *lock = this_cpu_ptr(&octeon_irq_ciu_spinlock); cd.p = irq_data_get_irq_chip_data(data); raw_spin_lock_irqsave(lock, flags); if (cd.s.line == 0) { - pen = &__get_cpu_var(octeon_irq_ciu0_en_mirror); + pen = this_cpu_ptr(&octeon_irq_ciu0_en_mirror); __set_bit(cd.s.bit, pen); /* * Must be visible to octeon_irq_ip{2,3}_ciu() before @@ -279,7 +279,7 @@ static void octeon_irq_ciu_enable_local(struct irq_data *data) wmb(); cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2), *pen); } else { - pen = &__get_cpu_var(octeon_irq_ciu1_en_mirror); + pen = this_cpu_ptr(&octeon_irq_ciu1_en_mirror); __set_bit(cd.s.bit, pen); /* * Must be visible to octeon_irq_ip{2,3}_ciu() before @@ -296,13 +296,13 @@ static void octeon_irq_ciu_disable_local(struct irq_data *data) unsigned long *pen; unsigned long flags; union octeon_ciu_chip_data cd; - raw_spinlock_t *lock = &__get_cpu_var(octeon_irq_ciu_spinlock); + raw_spinlock_t *lock = this_cpu_ptr(&octeon_irq_ciu_spinlock); cd.p = irq_data_get_irq_chip_data(data); raw_spin_lock_irqsave(lock, flags); if (cd.s.line == 0) { - pen = &__get_cpu_var(octeon_irq_ciu0_en_mirror); + pen = this_cpu_ptr(&octeon_irq_ciu0_en_mirror); __clear_bit(cd.s.bit, pen); /* * Must be visible to octeon_irq_ip{2,3}_ciu() before @@ -311,7 +311,7 @@ static void octeon_irq_ciu_disable_local(struct irq_data *data) wmb(); cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2), *pen); } else { - pen = &__get_cpu_var(octeon_irq_ciu1_en_mirror); + pen = this_cpu_ptr(&octeon_irq_ciu1_en_mirror); __clear_bit(cd.s.bit, pen); /* * Must be visible to octeon_irq_ip{2,3}_ciu() before @@ -431,11 +431,11 @@ static void octeon_irq_ciu_enable_local_v2(struct irq_data *data) if (cd.s.line == 0) { int index = cvmx_get_core_num() * 2; - set_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu0_en_mirror)); + set_bit(cd.s.bit, this_cpu_ptr(&octeon_irq_ciu0_en_mirror)); cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask); } else { int index = cvmx_get_core_num() * 2 + 1; - set_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu1_en_mirror)); + set_bit(cd.s.bit, this_cpu_ptr(&octeon_irq_ciu1_en_mirror)); cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask); } } @@ -450,11 +450,11 @@ static void octeon_irq_ciu_disable_local_v2(struct irq_data *data) if (cd.s.line == 0) { int index = cvmx_get_core_num() * 2; - clear_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu0_en_mirror)); + clear_bit(cd.s.bit, this_cpu_ptr(&octeon_irq_ciu0_en_mirror)); cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask); } else { int index = cvmx_get_core_num() * 2 + 1; - clear_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu1_en_mirror)); + clear_bit(cd.s.bit, this_cpu_ptr(&octeon_irq_ciu1_en_mirror)); cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask); } } @@ -1063,7 +1063,7 @@ static void octeon_irq_ip2_ciu(void) const unsigned long core_id = cvmx_get_core_num(); u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INTX_SUM0(core_id * 2)); - ciu_sum &= __get_cpu_var(octeon_irq_ciu0_en_mirror); + ciu_sum &= __this_cpu_read(octeon_irq_ciu0_en_mirror); if (likely(ciu_sum)) { int bit = fls64(ciu_sum) - 1; int irq = octeon_irq_ciu_to_irq[0][bit]; @@ -1080,7 +1080,7 @@ static void octeon_irq_ip3_ciu(void) { u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INT_SUM1); - ciu_sum &= __get_cpu_var(octeon_irq_ciu1_en_mirror); + ciu_sum &= __this_cpu_read(octeon_irq_ciu1_en_mirror); if (likely(ciu_sum)) { int bit = fls64(ciu_sum) - 1; int irq = octeon_irq_ciu_to_irq[1][bit]; @@ -1129,10 +1129,10 @@ static void octeon_irq_init_ciu_percpu(void) int coreid = cvmx_get_core_num(); - __get_cpu_var(octeon_irq_ciu0_en_mirror) = 0; - __get_cpu_var(octeon_irq_ciu1_en_mirror) = 0; + __this_cpu_write(octeon_irq_ciu0_en_mirror, 0); + __this_cpu_write(octeon_irq_ciu1_en_mirror, 0); wmb(); - raw_spin_lock_init(&__get_cpu_var(octeon_irq_ciu_spinlock)); + raw_spin_lock_init(this_cpu_ptr(&octeon_irq_ciu_spinlock)); /* * Disable All CIU Interrupts. The ones we need will be * enabled later. Read the SUM register so we know the write diff --git a/arch/mips/configs/sead3_defconfig b/arch/mips/configs/sead3_defconfig index 0abe681c11a07abdf4945aa46b8beeeac310fa65..dae9354b62563f8b4d97309861220b25b25be129 100644 --- a/arch/mips/configs/sead3_defconfig +++ b/arch/mips/configs/sead3_defconfig @@ -31,8 +31,8 @@ CONFIG_IP_PNP_BOOTP=y # CONFIG_IPV6 is not set # CONFIG_WIRELESS is not set CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y CONFIG_MTD=y -CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y CONFIG_MTD_CFI=y CONFIG_MTD_CFI_INTELEXT=y diff --git a/arch/mips/configs/sead3micro_defconfig b/arch/mips/configs/sead3micro_defconfig index 2a0da5bf4b64ea84f798e38cc3ad234e8ec16fd2..cd91a775c74efc9afc8ab8caf16e9647772647a5 100644 --- a/arch/mips/configs/sead3micro_defconfig +++ b/arch/mips/configs/sead3micro_defconfig @@ -32,8 +32,8 @@ CONFIG_IP_PNP_BOOTP=y # CONFIG_IPV6 is not set # CONFIG_WIRELESS is not set CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y CONFIG_MTD=y -CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y CONFIG_MTD_CFI=y CONFIG_MTD_CFI_INTELEXT=y diff --git a/arch/mips/include/asm/Kbuild b/arch/mips/include/asm/Kbuild index 57012ef1f51ea102faf35f911009aa14ce846a98..72e1cf1cab0057f6f5f9fae5dba18308afbc140e 100644 --- a/arch/mips/include/asm/Kbuild +++ b/arch/mips/include/asm/Kbuild @@ -1,6 +1,7 @@ # MIPS headers generic-y += cputime.h generic-y += current.h +generic-y += dma-contiguous.h generic-y += emergency-restart.h generic-y += hash.h generic-y += irq_work.h diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h index e079598ae0516b9db2ae674ae15e89e00527d422..3325f3eb248c4cebabe44b9579d733f6a65046ae 100644 --- a/arch/mips/include/asm/cpu-features.h +++ b/arch/mips/include/asm/cpu-features.h @@ -231,6 +231,16 @@ #define cpu_has_clo_clz cpu_has_mips_r #endif +/* + * MIPS32 R2, MIPS64 R2, Loongson 3A and Octeon have WSBH. + * MIPS64 R2, Loongson 3A and Octeon have WSBH, DSBH and DSHD. + * This indicates the availability of WSBH and in case of 64 bit CPUs also + * DSBH and DSHD. + */ +#ifndef cpu_has_wsbh +#define cpu_has_wsbh cpu_has_mips_r2 +#endif + #ifndef cpu_has_dsp #define cpu_has_dsp (cpu_data[0].ases & MIPS_ASE_DSP) #endif diff --git a/arch/mips/include/asm/cpu-info.h b/arch/mips/include/asm/cpu-info.h index d5f42c168001c69a21a37cfcf066f0892a0cdfc0..a6c9ccb33c5c9a35ceaac1485fb10f02a97da4b2 100644 --- a/arch/mips/include/asm/cpu-info.h +++ b/arch/mips/include/asm/cpu-info.h @@ -79,6 +79,11 @@ struct cpuinfo_mips { #define NUM_WATCH_REGS 4 u16 watch_reg_masks[NUM_WATCH_REGS]; unsigned int kscratch_mask; /* Usable KScratch mask. */ + /* + * Cache Coherency attribute for write-combine memory writes. + * (shifted by _CACHE_SHIFT) + */ + unsigned int writecombine; } __attribute__((aligned(SMP_CACHE_BYTES))); extern struct cpuinfo_mips cpu_data[]; diff --git a/arch/mips/include/asm/fpu_emulator.h b/arch/mips/include/asm/fpu_emulator.h index 0195745b4b1b47384ef324e52ce14577d4137677..3ee34771330718ec4c674c738f21a9dfb30af515 100644 --- a/arch/mips/include/asm/fpu_emulator.h +++ b/arch/mips/include/asm/fpu_emulator.h @@ -33,17 +33,17 @@ #ifdef CONFIG_DEBUG_FS struct mips_fpu_emulator_stats { - local_t emulated; - local_t loads; - local_t stores; - local_t cp1ops; - local_t cp1xops; - local_t errors; - local_t ieee754_inexact; - local_t ieee754_underflow; - local_t ieee754_overflow; - local_t ieee754_zerodiv; - local_t ieee754_invalidop; + unsigned long emulated; + unsigned long loads; + unsigned long stores; + unsigned long cp1ops; + unsigned long cp1xops; + unsigned long errors; + unsigned long ieee754_inexact; + unsigned long ieee754_underflow; + unsigned long ieee754_overflow; + unsigned long ieee754_zerodiv; + unsigned long ieee754_invalidop; }; DECLARE_PER_CPU(struct mips_fpu_emulator_stats, fpuemustats); @@ -51,7 +51,7 @@ DECLARE_PER_CPU(struct mips_fpu_emulator_stats, fpuemustats); #define MIPS_FPU_EMU_INC_STATS(M) \ do { \ preempt_disable(); \ - __local_inc(&__get_cpu_var(fpuemustats).M); \ + __this_cpu_inc(fpuemustats.M); \ preempt_enable(); \ } while (0) diff --git a/arch/mips/include/asm/mach-au1x00/cpu-feature-overrides.h b/arch/mips/include/asm/mach-au1x00/cpu-feature-overrides.h index 09f45e6afade2d5c2d86ab2fed53c928ceed32d3..c5b6eef0efa7b8477ef062953d243a9507d0986d 100644 --- a/arch/mips/include/asm/mach-au1x00/cpu-feature-overrides.h +++ b/arch/mips/include/asm/mach-au1x00/cpu-feature-overrides.h @@ -8,6 +8,12 @@ #define __ASM_MACH_AU1X00_CPU_FEATURE_OVERRIDES_H #define cpu_has_tlb 1 +#define cpu_has_tlbinv 0 +#define cpu_has_segments 0 +#define cpu_has_eva 0 +#define cpu_has_htw 0 +#define cpu_has_rixiex 0 +#define cpu_has_maar 0 #define cpu_has_4kex 1 #define cpu_has_3k_cache 0 #define cpu_has_4k_cache 1 @@ -28,6 +34,8 @@ #define cpu_has_mdmx 0 #define cpu_has_mips3d 0 #define cpu_has_smartmips 0 +#define cpu_has_rixi 0 +#define cpu_has_mmips 0 #define cpu_has_vtag_icache 0 #define cpu_has_dc_aliases 0 #define cpu_has_ic_fills_f_dc 1 @@ -50,4 +58,8 @@ #define cpu_dcache_line_size() 32 #define cpu_icache_line_size() 32 +#define cpu_has_perf_cntr_intr_bit 0 +#define cpu_has_vz 0 +#define cpu_has_msa 0 + #endif /* __ASM_MACH_AU1X00_CPU_FEATURE_OVERRIDES_H */ diff --git a/arch/mips/include/asm/mach-cavium-octeon/cpu-feature-overrides.h b/arch/mips/include/asm/mach-cavium-octeon/cpu-feature-overrides.h index cf80228728926d83b74e1c58366e740d6790d585..fa1f3cfbae8d70aa9dc0db696282bb1f8ed8f76f 100644 --- a/arch/mips/include/asm/mach-cavium-octeon/cpu-feature-overrides.h +++ b/arch/mips/include/asm/mach-cavium-octeon/cpu-feature-overrides.h @@ -57,6 +57,7 @@ #define cpu_has_vint 0 #define cpu_has_veic 0 #define cpu_hwrena_impl_bits 0xc0000000 +#define cpu_has_wsbh 1 #define cpu_has_rixi (cpu_data[0].cputype != CPU_CAVIUM_OCTEON) diff --git a/arch/mips/include/asm/mach-loongson/cpu-feature-overrides.h b/arch/mips/include/asm/mach-loongson/cpu-feature-overrides.h index c0f3ef45c2c14f0e658feee183eabc6f8141ea9f..7d28f95b0512ea27e1aeb7fac6f22ecc561a45fd 100644 --- a/arch/mips/include/asm/mach-loongson/cpu-feature-overrides.h +++ b/arch/mips/include/asm/mach-loongson/cpu-feature-overrides.h @@ -59,4 +59,6 @@ #define cpu_has_watch 1 #define cpu_has_local_ebase 0 +#define cpu_has_wsbh IS_ENABLED(CONFIG_CPU_LOONGSON3) + #endif /* __ASM_MACH_LOONGSON_CPU_FEATURE_OVERRIDES_H */ diff --git a/arch/mips/include/asm/pgtable-bits.h b/arch/mips/include/asm/pgtable-bits.h index e592f3687d6f9151739d67bb5f8ed0a9c3158c79..e747bfa0be7e0b100ebbdbb86c38750a865c427f 100644 --- a/arch/mips/include/asm/pgtable-bits.h +++ b/arch/mips/include/asm/pgtable-bits.h @@ -224,38 +224,52 @@ static inline uint64_t pte_to_entrylo(unsigned long pte_val) #if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) #define _CACHE_CACHABLE_NONCOHERENT 0 +#define _CACHE_UNCACHED_ACCELERATED _CACHE_UNCACHED #elif defined(CONFIG_CPU_SB1) /* No penalty for being coherent on the SB1, so just use it for "noncoherent" spaces, too. Shouldn't hurt. */ -#define _CACHE_UNCACHED (2<<_CACHE_SHIFT) -#define _CACHE_CACHABLE_COW (5<<_CACHE_SHIFT) #define _CACHE_CACHABLE_NONCOHERENT (5<<_CACHE_SHIFT) -#define _CACHE_UNCACHED_ACCELERATED (7<<_CACHE_SHIFT) #elif defined(CONFIG_CPU_LOONGSON3) /* Using COHERENT flag for NONCOHERENT doesn't hurt. */ -#define _CACHE_UNCACHED (2<<_CACHE_SHIFT) /* LOONGSON */ #define _CACHE_CACHABLE_NONCOHERENT (3<<_CACHE_SHIFT) /* LOONGSON */ #define _CACHE_CACHABLE_COHERENT (3<<_CACHE_SHIFT) /* LOONGSON-3 */ -#define _CACHE_UNCACHED_ACCELERATED (7<<_CACHE_SHIFT) /* LOONGSON */ -#else +#elif defined(CONFIG_MACH_JZ4740) + +/* Ingenic uses the WA bit to achieve write-combine memory writes */ +#define _CACHE_UNCACHED_ACCELERATED (1<<_CACHE_SHIFT) -#define _CACHE_CACHABLE_NO_WA (0<<_CACHE_SHIFT) /* R4600 only */ -#define _CACHE_CACHABLE_WA (1<<_CACHE_SHIFT) /* R4600 only */ -#define _CACHE_UNCACHED (2<<_CACHE_SHIFT) /* R4[0246]00 */ -#define _CACHE_CACHABLE_NONCOHERENT (3<<_CACHE_SHIFT) /* R4[0246]00 */ -#define _CACHE_CACHABLE_CE (4<<_CACHE_SHIFT) /* R4[04]00MC only */ -#define _CACHE_CACHABLE_COW (5<<_CACHE_SHIFT) /* R4[04]00MC only */ -#define _CACHE_CACHABLE_COHERENT (5<<_CACHE_SHIFT) /* MIPS32R2 CMP */ -#define _CACHE_CACHABLE_CUW (6<<_CACHE_SHIFT) /* R4[04]00MC only */ -#define _CACHE_UNCACHED_ACCELERATED (7<<_CACHE_SHIFT) /* R10000 only */ +#endif +#ifndef _CACHE_CACHABLE_NO_WA +#define _CACHE_CACHABLE_NO_WA (0<<_CACHE_SHIFT) +#endif +#ifndef _CACHE_CACHABLE_WA +#define _CACHE_CACHABLE_WA (1<<_CACHE_SHIFT) +#endif +#ifndef _CACHE_UNCACHED +#define _CACHE_UNCACHED (2<<_CACHE_SHIFT) +#endif +#ifndef _CACHE_CACHABLE_NONCOHERENT +#define _CACHE_CACHABLE_NONCOHERENT (3<<_CACHE_SHIFT) +#endif +#ifndef _CACHE_CACHABLE_CE +#define _CACHE_CACHABLE_CE (4<<_CACHE_SHIFT) +#endif +#ifndef _CACHE_CACHABLE_COW +#define _CACHE_CACHABLE_COW (5<<_CACHE_SHIFT) +#endif +#ifndef _CACHE_CACHABLE_CUW +#define _CACHE_CACHABLE_CUW (6<<_CACHE_SHIFT) +#endif +#ifndef _CACHE_UNCACHED_ACCELERATED +#define _CACHE_UNCACHED_ACCELERATED (7<<_CACHE_SHIFT) #endif #define __READABLE (_PAGE_SILENT_READ | _PAGE_ACCESSED | (cpu_has_rixi ? 0 : _PAGE_READ)) diff --git a/arch/mips/include/asm/pgtable.h b/arch/mips/include/asm/pgtable.h index df49a308085caa568c74e48a64a1eea0d7f2b845..d6d1928539b1a8ffdd93adda099d4a25f2ff2ee4 100644 --- a/arch/mips/include/asm/pgtable.h +++ b/arch/mips/include/asm/pgtable.h @@ -366,6 +366,16 @@ static inline pgprot_t pgprot_noncached(pgprot_t _prot) return __pgprot(prot); } +static inline pgprot_t pgprot_writecombine(pgprot_t _prot) +{ + unsigned long prot = pgprot_val(_prot); + + /* cpu_data[0].writecombine is already shifted by _CACHE_SHIFT */ + prot = (prot & ~_CACHE_MASK) | cpu_data[0].writecombine; + + return __pgprot(prot); +} + /* * Conversion functions: convert a page and protection to a page entry, * and a page entry and page directory to the page they refer to. diff --git a/arch/mips/include/asm/syscall.h b/arch/mips/include/asm/syscall.h index cdf68b33bd65ac5826ac369a4d06aeedc3d1fd90..bb7963753730d817117f209e6bac10f4943a0b56 100644 --- a/arch/mips/include/asm/syscall.h +++ b/arch/mips/include/asm/syscall.h @@ -129,7 +129,7 @@ extern const unsigned long sysn32_call_table[]; static inline int syscall_get_arch(void) { - int arch = EM_MIPS; + int arch = AUDIT_ARCH_MIPS; #ifdef CONFIG_64BIT if (!test_thread_flag(TIF_32BIT_REGS)) { arch |= __AUDIT_ARCH_64BIT; diff --git a/arch/mips/include/uapi/asm/swab.h b/arch/mips/include/uapi/asm/swab.h index ac9a8f9cd1fbfb53299e579661ce63d406cfee18..8f2d184dbe9f7adb43a32325e0061243c8f5fbf9 100644 --- a/arch/mips/include/uapi/asm/swab.h +++ b/arch/mips/include/uapi/asm/swab.h @@ -13,12 +13,16 @@ #define __SWAB_64_THRU_32__ -#if defined(__mips_isa_rev) && (__mips_isa_rev >= 2) +#if (defined(__mips_isa_rev) && (__mips_isa_rev >= 2)) || \ + defined(_MIPS_ARCH_LOONGSON3A) static inline __attribute_const__ __u16 __arch_swab16(__u16 x) { __asm__( + " .set push \n" + " .set arch=mips32r2 \n" " wsbh %0, %1 \n" + " .set pop \n" : "=r" (x) : "r" (x)); @@ -29,8 +33,11 @@ static inline __attribute_const__ __u16 __arch_swab16(__u16 x) static inline __attribute_const__ __u32 __arch_swab32(__u32 x) { __asm__( + " .set push \n" + " .set arch=mips32r2 \n" " wsbh %0, %1 \n" " rotr %0, %0, 16 \n" + " .set pop \n" : "=r" (x) : "r" (x)); @@ -46,8 +53,11 @@ static inline __attribute_const__ __u32 __arch_swab32(__u32 x) static inline __attribute_const__ __u64 __arch_swab64(__u64 x) { __asm__( - " dsbh %0, %1\n" - " dshd %0, %0" + " .set push \n" + " .set arch=mips64r2 \n" + " dsbh %0, %1 \n" + " dshd %0, %0 \n" + " .set pop \n" : "=r" (x) : "r" (x)); @@ -55,5 +65,5 @@ static inline __attribute_const__ __u64 __arch_swab64(__u64 x) } #define __arch_swab64 __arch_swab64 #endif /* __mips64 */ -#endif /* MIPS R2 or newer */ +#endif /* MIPS R2 or newer or Loongson 3A */ #endif /* _ASM_SWAB_H */ diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index e34b10be782e1dc4e81012078919029c9baad7e0..94c4a0c0a577909849326dd38999d30ef017ab99 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -764,6 +765,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) break; case PRID_REV_LOONGSON3A: c->cputype = CPU_LOONGSON3; + c->writecombine = _CACHE_UNCACHED_ACCELERATED; __cpu_name[cpu] = "ICT Loongson-3"; set_elf_platform(cpu, "loongson3a"); break; @@ -798,67 +800,83 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) static inline void cpu_probe_mips(struct cpuinfo_mips *c, unsigned int cpu) { + c->writecombine = _CACHE_UNCACHED_ACCELERATED; switch (c->processor_id & PRID_IMP_MASK) { case PRID_IMP_4KC: c->cputype = CPU_4KC; + c->writecombine = _CACHE_UNCACHED; __cpu_name[cpu] = "MIPS 4Kc"; break; case PRID_IMP_4KEC: case PRID_IMP_4KECR2: c->cputype = CPU_4KEC; + c->writecombine = _CACHE_UNCACHED; __cpu_name[cpu] = "MIPS 4KEc"; break; case PRID_IMP_4KSC: case PRID_IMP_4KSD: c->cputype = CPU_4KSC; + c->writecombine = _CACHE_UNCACHED; __cpu_name[cpu] = "MIPS 4KSc"; break; case PRID_IMP_5KC: c->cputype = CPU_5KC; + c->writecombine = _CACHE_UNCACHED; __cpu_name[cpu] = "MIPS 5Kc"; break; case PRID_IMP_5KE: c->cputype = CPU_5KE; + c->writecombine = _CACHE_UNCACHED; __cpu_name[cpu] = "MIPS 5KE"; break; case PRID_IMP_20KC: c->cputype = CPU_20KC; + c->writecombine = _CACHE_UNCACHED; __cpu_name[cpu] = "MIPS 20Kc"; break; case PRID_IMP_24K: c->cputype = CPU_24K; + c->writecombine = _CACHE_UNCACHED; __cpu_name[cpu] = "MIPS 24Kc"; break; case PRID_IMP_24KE: c->cputype = CPU_24K; + c->writecombine = _CACHE_UNCACHED; __cpu_name[cpu] = "MIPS 24KEc"; break; case PRID_IMP_25KF: c->cputype = CPU_25KF; + c->writecombine = _CACHE_UNCACHED; __cpu_name[cpu] = "MIPS 25Kc"; break; case PRID_IMP_34K: c->cputype = CPU_34K; + c->writecombine = _CACHE_UNCACHED; __cpu_name[cpu] = "MIPS 34Kc"; break; case PRID_IMP_74K: c->cputype = CPU_74K; + c->writecombine = _CACHE_UNCACHED; __cpu_name[cpu] = "MIPS 74Kc"; break; case PRID_IMP_M14KC: c->cputype = CPU_M14KC; + c->writecombine = _CACHE_UNCACHED; __cpu_name[cpu] = "MIPS M14Kc"; break; case PRID_IMP_M14KEC: c->cputype = CPU_M14KEC; + c->writecombine = _CACHE_UNCACHED; __cpu_name[cpu] = "MIPS M14KEc"; break; case PRID_IMP_1004K: c->cputype = CPU_1004K; + c->writecombine = _CACHE_UNCACHED; __cpu_name[cpu] = "MIPS 1004Kc"; break; case PRID_IMP_1074K: c->cputype = CPU_1074K; + c->writecombine = _CACHE_UNCACHED; __cpu_name[cpu] = "MIPS 1074Kc"; break; case PRID_IMP_INTERAPTIV_UP: @@ -932,6 +950,7 @@ static inline void cpu_probe_sibyte(struct cpuinfo_mips *c, unsigned int cpu) { decode_configs(c); + c->writecombine = _CACHE_UNCACHED_ACCELERATED; switch (c->processor_id & PRID_IMP_MASK) { case PRID_IMP_SB1: c->cputype = CPU_SB1; @@ -1063,6 +1082,7 @@ static inline void cpu_probe_ingenic(struct cpuinfo_mips *c, unsigned int cpu) switch (c->processor_id & PRID_IMP_MASK) { case PRID_IMP_JZRISC: c->cputype = CPU_JZRISC; + c->writecombine = _CACHE_UNCACHED_ACCELERATED; __cpu_name[cpu] = "Ingenic JZRISC"; break; default: @@ -1169,6 +1189,7 @@ void cpu_probe(void) c->processor_id = PRID_IMP_UNKNOWN; c->fpu_id = FPIR_IMP_NONE; c->cputype = CPU_UNKNOWN; + c->writecombine = _CACHE_UNCACHED; c->processor_id = read_c0_prid(); switch (c->processor_id & PRID_COMP_MASK) { diff --git a/arch/mips/kernel/kprobes.c b/arch/mips/kernel/kprobes.c index 1f8187ab0997be478f69b66eadb8ac4a6bf7e489..212f46f2014e06b36aed708b26215457be72ece7 100644 --- a/arch/mips/kernel/kprobes.c +++ b/arch/mips/kernel/kprobes.c @@ -224,7 +224,7 @@ static void save_previous_kprobe(struct kprobe_ctlblk *kcb) static void restore_previous_kprobe(struct kprobe_ctlblk *kcb) { - __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp; + __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp); kcb->kprobe_status = kcb->prev_kprobe.status; kcb->kprobe_old_SR = kcb->prev_kprobe.old_SR; kcb->kprobe_saved_SR = kcb->prev_kprobe.saved_SR; @@ -234,7 +234,7 @@ static void restore_previous_kprobe(struct kprobe_ctlblk *kcb) static void set_current_kprobe(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb) { - __get_cpu_var(current_kprobe) = p; + __this_cpu_write(current_kprobe, p); kcb->kprobe_saved_SR = kcb->kprobe_old_SR = (regs->cp0_status & ST0_IE); kcb->kprobe_saved_epc = regs->cp0_epc; } @@ -385,7 +385,7 @@ static int __kprobes kprobe_handler(struct pt_regs *regs) ret = 1; goto no_kprobe; } - p = __get_cpu_var(current_kprobe); + p = __this_cpu_read(current_kprobe); if (p->break_handler && p->break_handler(p, regs)) goto ss_probe; } diff --git a/arch/mips/kernel/perf_event_mipsxx.c b/arch/mips/kernel/perf_event_mipsxx.c index b63f2482f2881c24a3d62d2f95ca9a2eb5cf2799..a8f9cdc6f8b0417583d7433c116e1916cf5b7f7d 100644 --- a/arch/mips/kernel/perf_event_mipsxx.c +++ b/arch/mips/kernel/perf_event_mipsxx.c @@ -340,7 +340,7 @@ static int mipsxx_pmu_alloc_counter(struct cpu_hw_events *cpuc, static void mipsxx_pmu_enable_event(struct hw_perf_event *evt, int idx) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); WARN_ON(idx < 0 || idx >= mipspmu.num_counters); @@ -360,7 +360,7 @@ static void mipsxx_pmu_enable_event(struct hw_perf_event *evt, int idx) static void mipsxx_pmu_disable_event(int idx) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); unsigned long flags; WARN_ON(idx < 0 || idx >= mipspmu.num_counters); @@ -460,7 +460,7 @@ static void mipspmu_stop(struct perf_event *event, int flags) static int mipspmu_add(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc = &event->hw; int idx; int err = 0; @@ -496,7 +496,7 @@ static int mipspmu_add(struct perf_event *event, int flags) static void mipspmu_del(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc = &event->hw; int idx = hwc->idx; @@ -1275,7 +1275,7 @@ static int __hw_perf_event_init(struct perf_event *event) static void pause_local_counters(void) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int ctr = mipspmu.num_counters; unsigned long flags; @@ -1291,7 +1291,7 @@ static void pause_local_counters(void) static void resume_local_counters(void) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int ctr = mipspmu.num_counters; do { @@ -1302,7 +1302,7 @@ static void resume_local_counters(void) static int mipsxx_pmu_handle_shared_irq(void) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct perf_sample_data data; unsigned int counters = mipspmu.num_counters; u64 counter; diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index f7aac5b57b4b8e29470d0d2dd5ee37db4523398e..9d1487d832932a0b3dcb4d2c694e773e753a8767 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -780,9 +780,7 @@ asmlinkage long syscall_trace_enter(struct pt_regs *regs, long syscall) if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_enter(regs, regs->regs[2]); - audit_syscall_entry(syscall_get_arch(), - syscall, - regs->regs[4], regs->regs[5], + audit_syscall_entry(syscall, regs->regs[4], regs->regs[5], regs->regs[6], regs->regs[7]); return syscall; } diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 7c1fe2b42d405fed3c1ca5c61a3bfe743af7a6c3..b3b8f0d9d4a77b9ee4d2f02e0a2761a281ee87b1 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include @@ -476,6 +478,7 @@ static void __init bootmem_init(void) * o bootmem_init() * o sparse_init() * o paging_init() + * o dma_continguous_reserve() * * At this stage the bootmem allocator is ready to use. * @@ -609,6 +612,7 @@ static void __init request_crashkernel(struct resource *res) static void __init arch_mem_init(char **cmdline_p) { + struct memblock_region *reg; extern void plat_mem_setup(void); /* call board setup routine */ @@ -675,6 +679,11 @@ static void __init arch_mem_init(char **cmdline_p) sparse_init(); plat_swiotlb_setup(); paging_init(); + + dma_contiguous_reserve(PFN_PHYS(max_low_pfn)); + /* Tell bootmem about cma reserved memblock section */ + for_each_memblock(reserved, reg) + reserve_bootmem(reg->base, reg->size, BOOTMEM_DEFAULT); } static void __init resource_init(void) diff --git a/arch/mips/kernel/smp-bmips.c b/arch/mips/kernel/smp-bmips.c index df9e2bd9b2c292e0497cdd0daba5217589575c2c..06bb5ed6d80a68728b56329db41a6531d377eca8 100644 --- a/arch/mips/kernel/smp-bmips.c +++ b/arch/mips/kernel/smp-bmips.c @@ -346,7 +346,7 @@ static irqreturn_t bmips43xx_ipi_interrupt(int irq, void *dev_id) int action, cpu = irq - IPI0_IRQ; spin_lock_irqsave(&ipi_lock, flags); - action = __get_cpu_var(ipi_action_mask); + action = __this_cpu_read(ipi_action_mask); per_cpu(ipi_action_mask, cpu) = 0; clear_c0_cause(cpu ? C_SW1 : C_SW0); spin_unlock_irqrestore(&ipi_lock, flags); diff --git a/arch/mips/lantiq/Kconfig b/arch/mips/lantiq/Kconfig index c0021912131ebffe825232bada28332e6cd990fb..e10d33342b30209c816d7ee929ce11a894531f89 100644 --- a/arch/mips/lantiq/Kconfig +++ b/arch/mips/lantiq/Kconfig @@ -30,6 +30,7 @@ choice config DT_EASY50712 bool "Easy50712" depends on SOC_XWAY + select BUILTIN_DTB endchoice config PCI_LANTIQ diff --git a/arch/mips/lantiq/Makefile b/arch/mips/lantiq/Makefile index d6bdc579419fb2bbc7bfc0e7390a321cb404c995..690257ab86d60bf2fc8f7ead3268d483e8fbfb3f 100644 --- a/arch/mips/lantiq/Makefile +++ b/arch/mips/lantiq/Makefile @@ -6,8 +6,6 @@ obj-y := irq.o clk.o prom.o -obj-y += dts/ - obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-$(CONFIG_SOC_TYPE_XWAY) += xway/ diff --git a/arch/mips/lantiq/dts/Makefile b/arch/mips/lantiq/dts/Makefile deleted file mode 100644 index 6fa72dd641b299739f75568d5ad4c469852c49a4..0000000000000000000000000000000000000000 --- a/arch/mips/lantiq/dts/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_DT_EASY50712) := easy50712.dtb.o diff --git a/arch/mips/lib/csum_partial.S b/arch/mips/lib/csum_partial.S index 9901237563c58922d213b9af10b9ed3c504b18e5..4c721e247ac9a001f4cc4c2335da851143ae1e8a 100644 --- a/arch/mips/lib/csum_partial.S +++ b/arch/mips/lib/csum_partial.S @@ -277,9 +277,12 @@ LEAF(csum_partial) #endif /* odd buffer alignment? */ -#ifdef CONFIG_CPU_MIPSR2 +#if defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_LOONGSON3) + .set push + .set arch=mips32r2 wsbh v1, sum movn sum, v1, t7 + .set pop #else beqz t7, 1f /* odd buffer alignment? */ lui v1, 0x00ff @@ -726,9 +729,12 @@ LEAF(csum_partial) addu sum, v1 #endif -#ifdef CONFIG_CPU_MIPSR2 +#if defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_LOONGSON3) + .set push + .set arch=mips32r2 wsbh v1, sum movn sum, v1, odd + .set pop #else beqz odd, 1f /* odd buffer alignment? */ lui v1, 0x00ff diff --git a/arch/mips/loongson/loongson-3/smp.c b/arch/mips/loongson/loongson-3/smp.c index 74e827b4ec8f11aa208844d738d2b74afa1a1e7c..d8c63af6c7ccea9c69a4985ae14d2f0c263e7281 100644 --- a/arch/mips/loongson/loongson-3/smp.c +++ b/arch/mips/loongson/loongson-3/smp.c @@ -299,16 +299,16 @@ static void loongson3_init_secondary(void) per_cpu(cpu_state, cpu) = CPU_ONLINE; i = 0; - __get_cpu_var(core0_c0count) = 0; + __this_cpu_write(core0_c0count, 0); loongson3_send_ipi_single(0, SMP_ASK_C0COUNT); - while (!__get_cpu_var(core0_c0count)) { + while (!__this_cpu_read(core0_c0count)) { i++; cpu_relax(); } if (i > MAX_LOOPS) i = MAX_LOOPS; - initcount = __get_cpu_var(core0_c0count) + i; + initcount = __this_cpu_read(core0_c0count) + i; write_c0_count(initcount); } diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c index 44b6dff5aba2a09e92715aab976c2ac37619e729..33ba3c558fe4ff31a0693457ea28a71ae4b5fafa 100644 --- a/arch/mips/mm/dma-default.c +++ b/arch/mips/mm/dma-default.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -128,23 +129,30 @@ static void *mips_dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t * dma_handle, gfp_t gfp, struct dma_attrs *attrs) { void *ret; + struct page *page = NULL; + unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT; if (dma_alloc_from_coherent(dev, size, dma_handle, &ret)) return ret; gfp = massage_gfp_flags(dev, gfp); - ret = (void *) __get_free_pages(gfp, get_order(size)); - - if (ret) { - memset(ret, 0, size); - *dma_handle = plat_map_dma_mem(dev, ret, size); - - if (!plat_device_is_coherent(dev)) { - dma_cache_wback_inv((unsigned long) ret, size); - if (!hw_coherentio) - ret = UNCAC_ADDR(ret); - } + if (IS_ENABLED(CONFIG_DMA_CMA) && !(gfp & GFP_ATOMIC)) + page = dma_alloc_from_contiguous(dev, + count, get_order(size)); + if (!page) + page = alloc_pages(gfp, get_order(size)); + + if (!page) + return NULL; + + ret = page_address(page); + memset(ret, 0, size); + *dma_handle = plat_map_dma_mem(dev, ret, size); + if (!plat_device_is_coherent(dev)) { + dma_cache_wback_inv((unsigned long) ret, size); + if (!hw_coherentio) + ret = UNCAC_ADDR(ret); } return ret; @@ -164,6 +172,8 @@ static void mips_dma_free_coherent(struct device *dev, size_t size, void *vaddr, { unsigned long addr = (unsigned long) vaddr; int order = get_order(size); + unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT; + struct page *page = NULL; if (dma_release_from_coherent(dev, order, vaddr)) return; @@ -173,7 +183,10 @@ static void mips_dma_free_coherent(struct device *dev, size_t size, void *vaddr, if (!plat_device_is_coherent(dev) && !hw_coherentio) addr = CAC_ADDR(addr); - free_pages(addr, get_order(size)); + page = virt_to_page((void *) addr); + + if (!dma_release_from_contiguous(dev, page, count)) + __free_pages(page, get_order(size)); } static inline void __dma_sync_virtual(void *addr, size_t size, diff --git a/arch/mips/mti-sead3/Makefile b/arch/mips/mti-sead3/Makefile index 071786fa234ba5652cb290f9254f0e80ecebf6ce..febf4334545e5dc21cae2baae586e3067b064c4f 100644 --- a/arch/mips/mti-sead3/Makefile +++ b/arch/mips/mti-sead3/Makefile @@ -19,9 +19,5 @@ obj-y += sead3-i2c-dev.o sead3-i2c.o \ obj-$(CONFIG_EARLY_PRINTK) += sead3-console.o obj-$(CONFIG_USB_EHCI_HCD) += sead3-ehci.o -obj-$(CONFIG_OF) += sead3.dtb.o CFLAGS_sead3-setup.o = -I$(src)/../../../scripts/dtc/libfdt - -$(obj)/%.dtb: $(obj)/%.dts - $(call if_changed,dtc) diff --git a/arch/mips/net/bpf_jit.c b/arch/mips/net/bpf_jit.c index 7edc08398c4a9e04dabb5ff9401ca106b6f55312..9b55143d19dbad39ad8b7cd7f1b9ced90e8ab2cb 100644 --- a/arch/mips/net/bpf_jit.c +++ b/arch/mips/net/bpf_jit.c @@ -163,6 +163,19 @@ do { \ (ctx)->idx++; \ } while (0) +/* + * Similar to emit_instr but it must be used when we need to emit + * 32-bit or 64-bit instructions + */ +#define emit_long_instr(ctx, func, ...) \ +do { \ + if ((ctx)->target != NULL) { \ + u32 *p = &(ctx)->target[ctx->idx]; \ + UASM_i_##func(&p, ##__VA_ARGS__); \ + } \ + (ctx)->idx++; \ +} while (0) + /* Determine if immediate is within the 16-bit signed range */ static inline bool is_range16(s32 imm) { @@ -218,13 +231,6 @@ static inline void emit_ori(unsigned int dst, unsigned src, u32 imm, } } - -static inline void emit_daddu(unsigned int dst, unsigned int src1, - unsigned int src2, struct jit_ctx *ctx) -{ - emit_instr(ctx, daddu, dst, src1, src2); -} - static inline void emit_daddiu(unsigned int dst, unsigned int src, int imm, struct jit_ctx *ctx) { @@ -283,11 +289,7 @@ static inline void emit_xori(ptr dst, ptr src, u32 imm, struct jit_ctx *ctx) static inline void emit_stack_offset(int offset, struct jit_ctx *ctx) { - if (config_enabled(CONFIG_64BIT)) - emit_instr(ctx, daddiu, r_sp, r_sp, offset); - else - emit_instr(ctx, addiu, r_sp, r_sp, offset); - + emit_long_instr(ctx, ADDIU, r_sp, r_sp, offset); } static inline void emit_subu(unsigned int dst, unsigned int src1, @@ -365,10 +367,7 @@ static inline void emit_store_stack_reg(ptr reg, ptr base, unsigned int offset, struct jit_ctx *ctx) { - if (config_enabled(CONFIG_64BIT)) - emit_instr(ctx, sd, reg, offset, base); - else - emit_instr(ctx, sw, reg, offset, base); + emit_long_instr(ctx, SW, reg, offset, base); } static inline void emit_store(ptr reg, ptr base, unsigned int offset, @@ -381,10 +380,7 @@ static inline void emit_load_stack_reg(ptr reg, ptr base, unsigned int offset, struct jit_ctx *ctx) { - if (config_enabled(CONFIG_64BIT)) - emit_instr(ctx, ld, reg, offset, base); - else - emit_instr(ctx, lw, reg, offset, base); + emit_long_instr(ctx, LW, reg, offset, base); } static inline void emit_load(unsigned int reg, unsigned int base, @@ -458,10 +454,7 @@ static inline void emit_load_ptr(unsigned int dst, unsigned int src, int imm, struct jit_ctx *ctx) { /* src contains the base addr of the 32/64-pointer */ - if (config_enabled(CONFIG_64BIT)) - emit_instr(ctx, ld, dst, imm, src); - else - emit_instr(ctx, lw, dst, imm, src); + emit_long_instr(ctx, LW, dst, imm, src); } /* load a function pointer to register */ @@ -483,10 +476,7 @@ static inline void emit_load_func(unsigned int reg, ptr imm, /* Move to real MIPS register */ static inline void emit_reg_move(ptr dst, ptr src, struct jit_ctx *ctx) { - if (config_enabled(CONFIG_64BIT)) - emit_daddu(dst, src, r_zero, ctx); - else - emit_addu(dst, src, r_zero, ctx); + emit_long_instr(ctx, ADDU, dst, src, r_zero); } /* Move to JIT (32-bit) register */ @@ -623,10 +613,7 @@ static void save_bpf_jit_regs(struct jit_ctx *ctx, unsigned offset) if (ctx->flags & SEEN_MEM) { if (real_off % (RSIZE * 2)) real_off += RSIZE; - if (config_enabled(CONFIG_64BIT)) - emit_daddiu(r_M, r_sp, real_off, ctx); - else - emit_addiu(r_M, r_sp, real_off, ctx); + emit_long_instr(ctx, ADDIU, r_M, r_sp, real_off); } } @@ -1241,7 +1228,7 @@ static int build_body(struct jit_ctx *ctx) emit_half_load(r_A, r_skb, off, ctx); #ifdef CONFIG_CPU_LITTLE_ENDIAN /* This needs little endian fixup */ - if (cpu_has_mips_r2) { + if (cpu_has_wsbh) { /* R2 and later have the wsbh instruction */ emit_wsbh(r_A, r_A, ctx); } else { diff --git a/arch/mips/netlogic/Kconfig b/arch/mips/netlogic/Kconfig index 4eb683aef7d7669bf86cae46a45b5c352fbfab39..0823321c10e0744be05d11236b2ac47f2e5e275e 100644 --- a/arch/mips/netlogic/Kconfig +++ b/arch/mips/netlogic/Kconfig @@ -4,6 +4,7 @@ if NLM_XLP_BOARD config DT_XLP_EVP bool "Built-in device tree for XLP EVP boards" default y + select BUILTIN_DTB help Add an FDT blob for XLP EVP boards into the kernel. This DTB will be used if the firmware does not pass in a DTB @@ -13,6 +14,7 @@ config DT_XLP_EVP config DT_XLP_SVP bool "Built-in device tree for XLP SVP boards" default y + select BUILTIN_DTB help Add an FDT blob for XLP VP boards into the kernel. This DTB will be used if the firmware does not pass in a DTB @@ -22,6 +24,7 @@ config DT_XLP_SVP config DT_XLP_FVP bool "Built-in device tree for XLP FVP boards" default y + select BUILTIN_DTB help Add an FDT blob for XLP FVP board into the kernel. This DTB will be used if the firmware does not pass in a DTB @@ -31,6 +34,7 @@ config DT_XLP_FVP config DT_XLP_GVP bool "Built-in device tree for XLP GVP boards" default y + select BUILTIN_DTB help Add an FDT blob for XLP GVP board into the kernel. This DTB will be used if the firmware does not pass in a DTB diff --git a/arch/mips/netlogic/Makefile b/arch/mips/netlogic/Makefile index 7602d1386614ee2d384b421c9b415a093667cf85..36d169b2ca6d34f7185c491746c2e773206c301d 100644 --- a/arch/mips/netlogic/Makefile +++ b/arch/mips/netlogic/Makefile @@ -1,4 +1,3 @@ obj-$(CONFIG_NLM_COMMON) += common/ obj-$(CONFIG_CPU_XLR) += xlr/ obj-$(CONFIG_CPU_XLP) += xlp/ -obj-$(CONFIG_CPU_XLP) += dts/ diff --git a/arch/mips/netlogic/dts/Makefile b/arch/mips/netlogic/dts/Makefile deleted file mode 100644 index 25c8e873ee2577464f47f2475c1c26704341899f..0000000000000000000000000000000000000000 --- a/arch/mips/netlogic/dts/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -obj-$(CONFIG_DT_XLP_EVP) := xlp_evp.dtb.o -obj-$(CONFIG_DT_XLP_SVP) += xlp_svp.dtb.o -obj-$(CONFIG_DT_XLP_FVP) += xlp_fvp.dtb.o -obj-$(CONFIG_DT_XLP_GVP) += xlp_gvp.dtb.o diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig index 4a296655f4469391b17449fe3642c1d142308cc0..77e8a9620e183023bd67467ee8b8a1e468428220 100644 --- a/arch/mips/ralink/Kconfig +++ b/arch/mips/ralink/Kconfig @@ -42,18 +42,22 @@ choice config DTB_RT2880_EVAL bool "RT2880 eval kit" depends on SOC_RT288X + select BUILTIN_DTB config DTB_RT305X_EVAL bool "RT305x eval kit" depends on SOC_RT305X + select BUILTIN_DTB config DTB_RT3883_EVAL bool "RT3883 eval kit" depends on SOC_RT3883 + select BUILTIN_DTB config DTB_MT7620A_EVAL bool "MT7620A eval kit" depends on SOC_MT7620 + select BUILTIN_DTB endchoice diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile index 98ae349827be8068943e16db9e851cbfd1cc4501..2c09c8aa0ae24e05ab18fa9721f6a70865bceb92 100644 --- a/arch/mips/ralink/Makefile +++ b/arch/mips/ralink/Makefile @@ -16,5 +16,3 @@ obj-$(CONFIG_SOC_RT3883) += rt3883.o obj-$(CONFIG_SOC_MT7620) += mt7620.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o - -obj-y += dts/ diff --git a/arch/mips/ralink/dts/Makefile b/arch/mips/ralink/dts/Makefile deleted file mode 100644 index 18194fa93e8065f0916c90ce706ae2cb4e072ae4..0000000000000000000000000000000000000000 --- a/arch/mips/ralink/dts/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -obj-$(CONFIG_DTB_RT2880_EVAL) := rt2880_eval.dtb.o -obj-$(CONFIG_DTB_RT305X_EVAL) := rt3052_eval.dtb.o -obj-$(CONFIG_DTB_RT3883_EVAL) := rt3883_eval.dtb.o -obj-$(CONFIG_DTB_MT7620A_EVAL) := mt7620a_eval.dtb.o diff --git a/arch/mips/ralink/timer.c b/arch/mips/ralink/timer.c index e38692a44e6975b524ddb0df4f08bdd6d0376db5..5bb29b3790ff07fe92bda3f8911da4392b0d8c24 100644 --- a/arch/mips/ralink/timer.c +++ b/arch/mips/ralink/timer.c @@ -58,7 +58,7 @@ static irqreturn_t rt_timer_irq(int irq, void *_rt) static int rt_timer_request(struct rt_timer *rt) { - int err = request_irq(rt->irq, rt_timer_irq, IRQF_DISABLED, + int err = request_irq(rt->irq, rt_timer_irq, 0, dev_name(rt->dev), rt); if (err) { dev_err(rt->dev, "failed to request irq\n"); diff --git a/arch/openrisc/include/asm/syscall.h b/arch/openrisc/include/asm/syscall.h index b752bb67891db2da9358abc3af1bc1f8b95c57cc..2db9f1cf0694c0f2c6bdaec77953f62fb4fe6372 100644 --- a/arch/openrisc/include/asm/syscall.h +++ b/arch/openrisc/include/asm/syscall.h @@ -19,6 +19,7 @@ #ifndef __ASM_OPENRISC_SYSCALL_H__ #define __ASM_OPENRISC_SYSCALL_H__ +#include #include #include @@ -71,4 +72,8 @@ syscall_set_arguments(struct task_struct *task, struct pt_regs *regs, memcpy(®s->gpr[3 + i], args, n * sizeof(args[0])); } +static inline int syscall_get_arch(void) +{ + return AUDIT_ARCH_OPENRISC; +} #endif diff --git a/arch/openrisc/include/uapi/asm/elf.h b/arch/openrisc/include/uapi/asm/elf.h index f02ea5830420c5cb0299dd669dc6fc0c36624f9d..88842760e66f9234dd654fd90f88f655b65313b8 100644 --- a/arch/openrisc/include/uapi/asm/elf.h +++ b/arch/openrisc/include/uapi/asm/elf.h @@ -55,9 +55,8 @@ typedef elf_greg_t elf_gregset_t[ELF_NGREG]; /* A placeholder; OR32 does not have fp support yes, so no fp regs for now. */ typedef unsigned long elf_fpregset_t; -/* This should be moved to include/linux/elf.h */ +/* EM_OPENRISC is defined in linux/elf-em.h */ #define EM_OR32 0x8472 -#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ /* * These are used to set parameters in the core dumps. diff --git a/arch/openrisc/kernel/ptrace.c b/arch/openrisc/kernel/ptrace.c index 71a2a0c34c65b0e24d4defb6af481e878647f7d4..4f59fa4e34e5f2c795c3d1e150520513dd78d7d3 100644 --- a/arch/openrisc/kernel/ptrace.c +++ b/arch/openrisc/kernel/ptrace.c @@ -187,8 +187,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) */ ret = -1L; - audit_syscall_entry(AUDIT_ARCH_OPENRISC, regs->gpr[11], - regs->gpr[3], regs->gpr[4], + audit_syscall_entry(regs->gpr[11], regs->gpr[3], regs->gpr[4], regs->gpr[5], regs->gpr[6]); return ret ? : regs->gpr[11]; diff --git a/arch/parisc/include/asm/syscall.h b/arch/parisc/include/asm/syscall.h index 8bdfd2c8c39f7c3023b5660988df34599c4b231a..a5eba95d87feb448178b095c1f9cecc707edd71c 100644 --- a/arch/parisc/include/asm/syscall.h +++ b/arch/parisc/include/asm/syscall.h @@ -3,6 +3,8 @@ #ifndef _ASM_PARISC_SYSCALL_H_ #define _ASM_PARISC_SYSCALL_H_ +#include +#include #include #include @@ -37,4 +39,13 @@ static inline void syscall_get_arguments(struct task_struct *tsk, } } +static inline int syscall_get_arch(void) +{ + int arch = AUDIT_ARCH_PARISC; +#ifdef CONFIG_64BIT + if (!is_compat_task()) + arch = AUDIT_ARCH_PARISC64; +#endif + return arch; +} #endif /*_ASM_PARISC_SYSCALL_H_*/ diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c index 92438c21d4532e4c55d93adace8da60b081e88cf..9585c81f755fc667370ed850cba16ee1ff91024c 100644 --- a/arch/parisc/kernel/ptrace.c +++ b/arch/parisc/kernel/ptrace.c @@ -280,14 +280,11 @@ long do_syscall_trace_enter(struct pt_regs *regs) #ifdef CONFIG_64BIT if (!is_compat_task()) - audit_syscall_entry(AUDIT_ARCH_PARISC64, - regs->gr[20], - regs->gr[26], regs->gr[25], - regs->gr[24], regs->gr[23]); + audit_syscall_entry(regs->gr[20], regs->gr[26], regs->gr[25], + regs->gr[24], regs->gr[23]); else #endif - audit_syscall_entry(AUDIT_ARCH_PARISC, - regs->gr[20] & 0xffffffff, + audit_syscall_entry(regs->gr[20] & 0xffffffff, regs->gr[26] & 0xffffffff, regs->gr[25] & 0xffffffff, regs->gr[24] & 0xffffffff, diff --git a/arch/powerpc/include/asm/cputime.h b/arch/powerpc/include/asm/cputime.h index 6c840ceab8201cc191f4699d15561c62b2e0c9c2..e2452550bcb16322ddab48582fa93a632bba80b5 100644 --- a/arch/powerpc/include/asm/cputime.h +++ b/arch/powerpc/include/asm/cputime.h @@ -58,10 +58,10 @@ static inline unsigned long cputime_to_jiffies(const cputime_t ct) static inline cputime_t cputime_to_scaled(const cputime_t ct) { if (cpu_has_feature(CPU_FTR_SPURR) && - __get_cpu_var(cputime_last_delta)) + __this_cpu_read(cputime_last_delta)) return (__force u64) ct * - __get_cpu_var(cputime_scaled_last_delta) / - __get_cpu_var(cputime_last_delta); + __this_cpu_read(cputime_scaled_last_delta) / + __this_cpu_read(cputime_last_delta); return ct; } diff --git a/arch/powerpc/include/asm/syscall.h b/arch/powerpc/include/asm/syscall.h index b54b2add07be99eb67a2a662231443063c8a996e..6fa2708da15304b7bc3ad10f3480e81a837532f8 100644 --- a/arch/powerpc/include/asm/syscall.h +++ b/arch/powerpc/include/asm/syscall.h @@ -13,7 +13,9 @@ #ifndef _ASM_SYSCALL_H #define _ASM_SYSCALL_H 1 +#include #include +#include /* ftrace syscalls requires exporting the sys_call_table */ #ifdef CONFIG_FTRACE_SYSCALLS @@ -86,4 +88,8 @@ static inline void syscall_set_arguments(struct task_struct *task, memcpy(®s->gpr[3 + i], args, n * sizeof(args[0])); } +static inline int syscall_get_arch(void) +{ + return is_32bit_task() ? AUDIT_ARCH_PPC : AUDIT_ARCH_PPC64; +} #endif /* _ASM_SYSCALL_H */ diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index bd70a51d5747594906f24911d46f8a7b2abfb489..e5dad9a9edc0792b5294c5a5d1ed7291f23d4c3e 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -747,7 +747,11 @@ void pci_process_bridge_OF_ranges(struct pci_controller *hose, break; } if (res != NULL) { - of_pci_range_to_resource(&range, dev, res); + res->name = dev->full_name; + res->flags = range.flags; + res->start = range.cpu_addr; + res->end = range.cpu_addr + range.size - 1; + res->parent = res->child = res->sibling = NULL; } } } diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index cdb404ea34688b581a788694ed1f8da43aed1f91..f21897b420576c63330eb5c62982ba0729229a85 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -1788,14 +1788,11 @@ long do_syscall_trace_enter(struct pt_regs *regs) #ifdef CONFIG_PPC64 if (!is_32bit_task()) - audit_syscall_entry(AUDIT_ARCH_PPC64, - regs->gpr[0], - regs->gpr[3], regs->gpr[4], + audit_syscall_entry(regs->gpr[0], regs->gpr[3], regs->gpr[4], regs->gpr[5], regs->gpr[6]); else #endif - audit_syscall_entry(AUDIT_ARCH_PPC, - regs->gpr[0], + audit_syscall_entry(regs->gpr[0], regs->gpr[3] & 0xffffffff, regs->gpr[4] & 0xffffffff, regs->gpr[5] & 0xffffffff, diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 296391395b955bb3e4ba25dc33458a55982d5546..f2cf1f90295b6b38e855b398a3e0a2180e82fe4e 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -58,6 +58,9 @@ config NO_IOPORT_MAP config PCI_QUIRKS def_bool n +config ARCH_SUPPORTS_UPROBES + def_bool 64BIT + config S390 def_bool y select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE @@ -97,6 +100,7 @@ config S390 select ARCH_WANT_IPC_PARSE_VERSION select BUILDTIME_EXTABLE_SORT select CLONE_BACKWARDS2 + select DYNAMIC_FTRACE if FUNCTION_TRACER select GENERIC_CLOCKEVENTS select GENERIC_CPU_DEVICES if !SMP select GENERIC_FIND_FIRST_BIT @@ -113,10 +117,11 @@ config S390 select HAVE_CMPXCHG_LOCAL select HAVE_C_RECORDMCOUNT select HAVE_DEBUG_KMEMLEAK - select HAVE_DYNAMIC_FTRACE + select HAVE_DYNAMIC_FTRACE if 64BIT + select HAVE_DYNAMIC_FTRACE_WITH_REGS if 64BIT select HAVE_FTRACE_MCOUNT_RECORD - select HAVE_FUNCTION_GRAPH_TRACER - select HAVE_FUNCTION_TRACER + select HAVE_FUNCTION_GRAPH_TRACER if 64BIT + select HAVE_FUNCTION_TRACER if 64BIT select HAVE_FUTEX_CMPXCHG if FUTEX select HAVE_KERNEL_BZIP2 select HAVE_KERNEL_GZIP diff --git a/arch/s390/Makefile b/arch/s390/Makefile index 874e6d6e9c5fe41e035f4fafb08f0cba44f92760..878e67973151b54c93c5eb078d53a4beef330f10 100644 --- a/arch/s390/Makefile +++ b/arch/s390/Makefile @@ -35,13 +35,16 @@ endif export LD_BFD -cflags-$(CONFIG_MARCH_G5) += -march=g5 -cflags-$(CONFIG_MARCH_Z900) += -march=z900 -cflags-$(CONFIG_MARCH_Z990) += -march=z990 -cflags-$(CONFIG_MARCH_Z9_109) += -march=z9-109 -cflags-$(CONFIG_MARCH_Z10) += -march=z10 -cflags-$(CONFIG_MARCH_Z196) += -march=z196 -cflags-$(CONFIG_MARCH_ZEC12) += -march=zEC12 +mflags-$(CONFIG_MARCH_G5) := -march=g5 +mflags-$(CONFIG_MARCH_Z900) := -march=z900 +mflags-$(CONFIG_MARCH_Z990) := -march=z990 +mflags-$(CONFIG_MARCH_Z9_109) := -march=z9-109 +mflags-$(CONFIG_MARCH_Z10) := -march=z10 +mflags-$(CONFIG_MARCH_Z196) := -march=z196 +mflags-$(CONFIG_MARCH_ZEC12) := -march=zEC12 + +aflags-y += $(mflags-y) +cflags-y += $(mflags-y) cflags-$(CONFIG_MARCH_G5_TUNE) += -mtune=g5 cflags-$(CONFIG_MARCH_Z900_TUNE) += -mtune=z900 diff --git a/arch/s390/include/asm/barrier.h b/arch/s390/include/asm/barrier.h index 19ff956b752baf751380e1ecf553b8983cdad56d..b5dce6544d76e3357e4e8629333817008a0ee183 100644 --- a/arch/s390/include/asm/barrier.h +++ b/arch/s390/include/asm/barrier.h @@ -15,11 +15,13 @@ #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES /* Fast-BCR without checkpoint synchronization */ -#define mb() do { asm volatile("bcr 14,0" : : : "memory"); } while (0) +#define __ASM_BARRIER "bcr 14,0\n" #else -#define mb() do { asm volatile("bcr 15,0" : : : "memory"); } while (0) +#define __ASM_BARRIER "bcr 15,0\n" #endif +#define mb() do { asm volatile(__ASM_BARRIER : : : "memory"); } while (0) + #define rmb() mb() #define wmb() mb() #define read_barrier_depends() do { } while(0) diff --git a/arch/s390/include/asm/cputime.h b/arch/s390/include/asm/cputime.h index 3001887f94b7a7c31ca01051db3f3cf9643e3ad0..f8c196984853e963c6e22dc6553b5f4cccdc2d23 100644 --- a/arch/s390/include/asm/cputime.h +++ b/arch/s390/include/asm/cputime.h @@ -8,8 +8,6 @@ #define _S390_CPUTIME_H #include -#include -#include #include @@ -167,28 +165,8 @@ static inline clock_t cputime64_to_clock_t(cputime64_t cputime) return clock; } -struct s390_idle_data { - int nohz_delay; - unsigned int sequence; - unsigned long long idle_count; - unsigned long long idle_time; - unsigned long long clock_idle_enter; - unsigned long long clock_idle_exit; - unsigned long long timer_idle_enter; - unsigned long long timer_idle_exit; -}; +cputime64_t arch_cpu_idle_time(int cpu); -DECLARE_PER_CPU(struct s390_idle_data, s390_idle); - -cputime64_t s390_get_idle_time(int cpu); - -#define arch_idle_time(cpu) s390_get_idle_time(cpu) - -static inline int s390_nohz_delay(int cpu) -{ - return __get_cpu_var(s390_idle).nohz_delay != 0; -} - -#define arch_needs_cpu(cpu) s390_nohz_delay(cpu) +#define arch_idle_time(cpu) arch_cpu_idle_time(cpu) #endif /* _S390_CPUTIME_H */ diff --git a/arch/s390/include/asm/dis.h b/arch/s390/include/asm/dis.h index 04a83f5773cd5638959c8d7187a4c0043ffca190..60323c21938bb5d97407e9f0cb35c6fcdd335597 100644 --- a/arch/s390/include/asm/dis.h +++ b/arch/s390/include/asm/dis.h @@ -13,12 +13,13 @@ #define OPERAND_FPR 0x2 /* Operand printed as %fx */ #define OPERAND_AR 0x4 /* Operand printed as %ax */ #define OPERAND_CR 0x8 /* Operand printed as %cx */ -#define OPERAND_DISP 0x10 /* Operand printed as displacement */ -#define OPERAND_BASE 0x20 /* Operand printed as base register */ -#define OPERAND_INDEX 0x40 /* Operand printed as index register */ -#define OPERAND_PCREL 0x80 /* Operand printed as pc-relative symbol */ -#define OPERAND_SIGNED 0x100 /* Operand printed as signed value */ -#define OPERAND_LENGTH 0x200 /* Operand printed as length (+1) */ +#define OPERAND_VR 0x10 /* Operand printed as %vx */ +#define OPERAND_DISP 0x20 /* Operand printed as displacement */ +#define OPERAND_BASE 0x40 /* Operand printed as base register */ +#define OPERAND_INDEX 0x80 /* Operand printed as index register */ +#define OPERAND_PCREL 0x100 /* Operand printed as pc-relative symbol */ +#define OPERAND_SIGNED 0x200 /* Operand printed as signed value */ +#define OPERAND_LENGTH 0x400 /* Operand printed as length (+1) */ struct s390_operand { diff --git a/arch/s390/include/asm/elf.h b/arch/s390/include/asm/elf.h index 78f4f8711d58a78f0a3e277b13719bd1514f38ea..f6e43d39e3d82e7c86e771078b752a88f998c481 100644 --- a/arch/s390/include/asm/elf.h +++ b/arch/s390/include/asm/elf.h @@ -102,6 +102,7 @@ #define HWCAP_S390_ETF3EH 256 #define HWCAP_S390_HIGH_GPRS 512 #define HWCAP_S390_TE 1024 +#define HWCAP_S390_VXRS 2048 /* * These are used to set parameters in the core dumps. @@ -225,6 +226,6 @@ int arch_setup_additional_pages(struct linux_binprm *, int); extern unsigned long arch_randomize_brk(struct mm_struct *mm); #define arch_randomize_brk arch_randomize_brk -void *fill_cpu_elf_notes(void *ptr, struct save_area *sa); +void *fill_cpu_elf_notes(void *ptr, struct save_area *sa, __vector128 *vxrs); #endif diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h index bf246dae1367333c109f7f28a60789c2f97deb84..3aef8afec336c822e2f3f44b76209e510ab510e0 100644 --- a/arch/s390/include/asm/ftrace.h +++ b/arch/s390/include/asm/ftrace.h @@ -4,6 +4,7 @@ #ifndef __ASSEMBLY__ extern void _mcount(void); +extern char ftrace_graph_caller_end; struct dyn_arch_ftrace { }; @@ -17,10 +18,8 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) #endif /* __ASSEMBLY__ */ -#ifdef CONFIG_64BIT -#define MCOUNT_INSN_SIZE 12 -#else -#define MCOUNT_INSN_SIZE 22 -#endif +#define MCOUNT_INSN_SIZE 18 + +#define ARCH_SUPPORTS_FTRACE_OPS 1 #endif /* _ASM_S390_FTRACE_H */ diff --git a/arch/s390/include/asm/idle.h b/arch/s390/include/asm/idle.h new file mode 100644 index 0000000000000000000000000000000000000000..6af037f574b87517073b9bd92219d860eedf3d1a --- /dev/null +++ b/arch/s390/include/asm/idle.h @@ -0,0 +1,26 @@ +/* + * Copyright IBM Corp. 2014 + * + * Author: Martin Schwidefsky + */ + +#ifndef _S390_IDLE_H +#define _S390_IDLE_H + +#include +#include + +struct s390_idle_data { + unsigned int sequence; + unsigned long long idle_count; + unsigned long long idle_time; + unsigned long long clock_idle_enter; + unsigned long long clock_idle_exit; + unsigned long long timer_idle_enter; + unsigned long long timer_idle_exit; +}; + +extern struct device_attribute dev_attr_idle_count; +extern struct device_attribute dev_attr_idle_time_us; + +#endif /* _S390_IDLE_H */ diff --git a/arch/s390/include/asm/ipl.h b/arch/s390/include/asm/ipl.h index c81661e756a0396bb8e90dee214492b154081d91..ece606c2ee8674111b81bf9c5a6342e8ae5db421 100644 --- a/arch/s390/include/asm/ipl.h +++ b/arch/s390/include/asm/ipl.h @@ -89,12 +89,12 @@ extern u32 ipl_flags; extern u32 dump_prefix_page; struct dump_save_areas { - struct save_area **areas; + struct save_area_ext **areas; int count; }; extern struct dump_save_areas dump_save_areas; -struct save_area *dump_save_area_create(int cpu); +struct save_area_ext *dump_save_area_create(int cpu); extern void do_reipl(void); extern void do_halt(void); diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index c4dd400a27917b7fa38a4e6daf66d91b5e88e688..b0d5f0a97a010f355d2071acb2a5af8d2de88574 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h @@ -51,6 +51,7 @@ enum interruption_class { IRQEXT_CMS, IRQEXT_CMC, IRQEXT_CMR, + IRQEXT_FTP, IRQIO_CIO, IRQIO_QAI, IRQIO_DAS, @@ -81,7 +82,7 @@ DECLARE_PER_CPU_SHARED_ALIGNED(struct irq_stat, irq_stat); static __always_inline void inc_irq_stat(enum interruption_class irq) { - __get_cpu_var(irq_stat).irqs[irq]++; + __this_cpu_inc(irq_stat.irqs[irq]); } struct ext_code { diff --git a/arch/s390/include/asm/kprobes.h b/arch/s390/include/asm/kprobes.h index 4176dfe0fba1c75ed253966ecab56eeea54c174b..98629173ce3b38bf07b07324df8515b1c5e285ad 100644 --- a/arch/s390/include/asm/kprobes.h +++ b/arch/s390/include/asm/kprobes.h @@ -84,6 +84,10 @@ int kprobe_fault_handler(struct pt_regs *regs, int trapnr); int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, void *data); +int probe_is_prohibited_opcode(u16 *insn); +int probe_get_fixup_type(u16 *insn); +int probe_is_insn_relative_long(u16 *insn); + #define flush_insn_slot(p) do { } while (0) #endif /* _ASM_S390_KPROBES_H */ diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h index 4349197ab9df9019ea5a23938f4b473e8d95442f..6cc51fe844108d712285ddfeb7b970a2f33d8329 100644 --- a/arch/s390/include/asm/lowcore.h +++ b/arch/s390/include/asm/lowcore.h @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef CONFIG_32BIT @@ -31,6 +32,11 @@ struct save_area { u32 ctrl_regs[16]; } __packed; +struct save_area_ext { + struct save_area sa; + __vector128 vx_regs[32]; +}; + struct _lowcore { psw_t restart_psw; /* 0x0000 */ psw_t restart_old_psw; /* 0x0008 */ @@ -183,6 +189,11 @@ struct save_area { u64 ctrl_regs[16]; } __packed; +struct save_area_ext { + struct save_area sa; + __vector128 vx_regs[32]; +}; + struct _lowcore { __u8 pad_0x0000[0x0014-0x0000]; /* 0x0000 */ __u32 ipl_parmblock_ptr; /* 0x0014 */ @@ -310,7 +321,10 @@ struct _lowcore { /* Extended facility list */ __u64 stfle_fac_list[32]; /* 0x0f00 */ - __u8 pad_0x1000[0x11b8-0x1000]; /* 0x1000 */ + __u8 pad_0x1000[0x11b0-0x1000]; /* 0x1000 */ + + /* Pointer to vector register save area */ + __u64 vector_save_area_addr; /* 0x11b0 */ /* 64 bit extparam used for pfault/diag 250: defined by architecture */ __u64 ext_params2; /* 0x11B8 */ @@ -334,9 +348,10 @@ struct _lowcore { /* Transaction abort diagnostic block */ __u8 pgm_tdb[256]; /* 0x1800 */ + __u8 pad_0x1900[0x1c00-0x1900]; /* 0x1900 */ - /* align to the top of the prefix area */ - __u8 pad_0x1900[0x2000-0x1900]; /* 0x1900 */ + /* Software defined save area for vector registers */ + __u8 vector_save_area[1024]; /* 0x1c00 */ } __packed; #endif /* CONFIG_32BIT */ diff --git a/arch/s390/include/asm/nmi.h b/arch/s390/include/asm/nmi.h index 35f8ec185616f07a1485b1bd7085133d3f299d07..3027a5a72b748ab776d1693df011da55a2049ea1 100644 --- a/arch/s390/include/asm/nmi.h +++ b/arch/s390/include/asm/nmi.h @@ -38,7 +38,7 @@ struct mci { __u32 pm : 1; /* 22 psw program mask and cc validity */ __u32 ia : 1; /* 23 psw instruction address validity */ __u32 fa : 1; /* 24 failing storage address validity */ - __u32 : 1; /* 25 */ + __u32 vr : 1; /* 25 vector register validity */ __u32 ec : 1; /* 26 external damage code validity */ __u32 fp : 1; /* 27 floating point register validity */ __u32 gr : 1; /* 28 general register validity */ diff --git a/arch/s390/include/asm/percpu.h b/arch/s390/include/asm/percpu.h index fa91e0097458d8d56cd3d41f24fd0a5a9f42d72a..933355e0d091b86025c411e793de63cede01fee6 100644 --- a/arch/s390/include/asm/percpu.h +++ b/arch/s390/include/asm/percpu.h @@ -31,7 +31,7 @@ pcp_op_T__ old__, new__, prev__; \ pcp_op_T__ *ptr__; \ preempt_disable(); \ - ptr__ = __this_cpu_ptr(&(pcp)); \ + ptr__ = raw_cpu_ptr(&(pcp)); \ prev__ = *ptr__; \ do { \ old__ = prev__; \ @@ -70,7 +70,7 @@ pcp_op_T__ val__ = (val); \ pcp_op_T__ old__, *ptr__; \ preempt_disable(); \ - ptr__ = __this_cpu_ptr(&(pcp)); \ + ptr__ = raw_cpu_ptr(&(pcp)); \ if (__builtin_constant_p(val__) && \ ((szcast)val__ > -129) && ((szcast)val__ < 128)) { \ asm volatile( \ @@ -97,7 +97,7 @@ pcp_op_T__ val__ = (val); \ pcp_op_T__ old__, *ptr__; \ preempt_disable(); \ - ptr__ = __this_cpu_ptr(&(pcp)); \ + ptr__ = raw_cpu_ptr(&(pcp)); \ asm volatile( \ op " %[old__],%[val__],%[ptr__]\n" \ : [old__] "=d" (old__), [ptr__] "+Q" (*ptr__) \ @@ -116,7 +116,7 @@ pcp_op_T__ val__ = (val); \ pcp_op_T__ old__, *ptr__; \ preempt_disable(); \ - ptr__ = __this_cpu_ptr(&(pcp)); \ + ptr__ = raw_cpu_ptr(&(pcp)); \ asm volatile( \ op " %[old__],%[val__],%[ptr__]\n" \ : [old__] "=d" (old__), [ptr__] "+Q" (*ptr__) \ @@ -138,7 +138,7 @@ pcp_op_T__ ret__; \ pcp_op_T__ *ptr__; \ preempt_disable(); \ - ptr__ = __this_cpu_ptr(&(pcp)); \ + ptr__ = raw_cpu_ptr(&(pcp)); \ ret__ = cmpxchg(ptr__, oval, nval); \ preempt_enable(); \ ret__; \ @@ -154,7 +154,7 @@ typeof(pcp) *ptr__; \ typeof(pcp) ret__; \ preempt_disable(); \ - ptr__ = __this_cpu_ptr(&(pcp)); \ + ptr__ = raw_cpu_ptr(&(pcp)); \ ret__ = xchg(ptr__, nval); \ preempt_enable(); \ ret__; \ @@ -173,8 +173,8 @@ typeof(pcp2) *p2__; \ int ret__; \ preempt_disable(); \ - p1__ = __this_cpu_ptr(&(pcp1)); \ - p2__ = __this_cpu_ptr(&(pcp2)); \ + p1__ = raw_cpu_ptr(&(pcp1)); \ + p2__ = raw_cpu_ptr(&(pcp2)); \ ret__ = __cmpxchg_double(p1__, p2__, o1__, o2__, n1__, n2__); \ preempt_enable(); \ ret__; \ diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index b7054356cc9843ba4e80d17bc38614614f3361e3..57c882761deaa2e543342e5bb68cdb5524851eef 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -217,7 +217,6 @@ extern unsigned long MODULES_END; */ /* Hardware bits in the page table entry */ -#define _PAGE_CO 0x100 /* HW Change-bit override */ #define _PAGE_PROTECT 0x200 /* HW read-only bit */ #define _PAGE_INVALID 0x400 /* HW invalid bit */ #define _PAGE_LARGE 0x800 /* Bit to mark a large pte */ @@ -234,8 +233,8 @@ extern unsigned long MODULES_END; #define __HAVE_ARCH_PTE_SPECIAL /* Set of bits not changed in pte_modify */ -#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_SPECIAL | _PAGE_CO | \ - _PAGE_DIRTY | _PAGE_YOUNG) +#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_SPECIAL | _PAGE_DIRTY | \ + _PAGE_YOUNG) /* * handle_pte_fault uses pte_present, pte_none and pte_file to find out the @@ -354,7 +353,6 @@ extern unsigned long MODULES_END; #define _REGION3_ENTRY_LARGE 0x400 /* RTTE-format control, large page */ #define _REGION3_ENTRY_RO 0x200 /* page protection bit */ -#define _REGION3_ENTRY_CO 0x100 /* change-recording override */ /* Bits in the segment table entry */ #define _SEGMENT_ENTRY_BITS 0xfffffffffffffe33UL @@ -371,7 +369,6 @@ extern unsigned long MODULES_END; #define _SEGMENT_ENTRY_YOUNG 0x1000 /* SW segment young bit */ #define _SEGMENT_ENTRY_SPLIT 0x0800 /* THP splitting bit */ #define _SEGMENT_ENTRY_LARGE 0x0400 /* STE-format control, large page */ -#define _SEGMENT_ENTRY_CO 0x0100 /* change-recording override */ #define _SEGMENT_ENTRY_READ 0x0002 /* SW segment read bit */ #define _SEGMENT_ENTRY_WRITE 0x0001 /* SW segment write bit */ @@ -873,8 +870,6 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, pgste = pgste_set_pte(ptep, pgste, entry); pgste_set_unlock(ptep, pgste); } else { - if (!(pte_val(entry) & _PAGE_INVALID) && MACHINE_HAS_EDAT1) - pte_val(entry) |= _PAGE_CO; *ptep = entry; } } @@ -1044,6 +1039,22 @@ static inline void __ptep_ipte_local(unsigned long address, pte_t *ptep) : "=m" (*ptep) : "m" (*ptep), "a" (pto), "a" (address)); } +static inline void __ptep_ipte_range(unsigned long address, int nr, pte_t *ptep) +{ + unsigned long pto = (unsigned long) ptep; + +#ifndef CONFIG_64BIT + /* pto in ESA mode must point to the start of the segment table */ + pto &= 0x7ffffc00; +#endif + /* Invalidate a range of ptes + global TLB flush of the ptes */ + do { + asm volatile( + " .insn rrf,0xb2210000,%2,%0,%1,0" + : "+a" (address), "+a" (nr) : "a" (pto) : "memory"); + } while (nr != 255); +} + static inline void ptep_flush_direct(struct mm_struct *mm, unsigned long address, pte_t *ptep) { diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index e568fc8a725062f707b7875c7c4010a21bd0fcfa..d559bdb03d18325f5c5fda2e97eb0136aeb1baf3 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -13,9 +13,11 @@ #define CIF_MCCK_PENDING 0 /* machine check handling is pending */ #define CIF_ASCE 1 /* user asce needs fixup / uaccess */ +#define CIF_NOHZ_DELAY 2 /* delay HZ disable for a tick */ #define _CIF_MCCK_PENDING (1<gprs[2]; } +static inline void instruction_pointer_set(struct pt_regs *regs, + unsigned long val) +{ + regs->psw.addr = val | PSW_ADDR_AMODE; +} + int regs_query_register_offset(const char *name); const char *regs_query_register_name(unsigned int offset); unsigned long regs_get_register(struct pt_regs *regs, unsigned int offset); diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index 089a49814c505b506ed1689230a470ebfc575d03..7736fdd725953c862f3df931723ed355d8b23f8b 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h @@ -55,8 +55,8 @@ extern void detect_memory_memblock(void); #define MACHINE_FLAG_LPP (1UL << 13) #define MACHINE_FLAG_TOPOLOGY (1UL << 14) #define MACHINE_FLAG_TE (1UL << 15) -#define MACHINE_FLAG_RRBM (1UL << 16) #define MACHINE_FLAG_TLB_LC (1UL << 17) +#define MACHINE_FLAG_VX (1UL << 18) #define MACHINE_IS_VM (S390_lowcore.machine_flags & MACHINE_FLAG_VM) #define MACHINE_IS_KVM (S390_lowcore.machine_flags & MACHINE_FLAG_KVM) @@ -78,8 +78,8 @@ extern void detect_memory_memblock(void); #define MACHINE_HAS_LPP (0) #define MACHINE_HAS_TOPOLOGY (0) #define MACHINE_HAS_TE (0) -#define MACHINE_HAS_RRBM (0) #define MACHINE_HAS_TLB_LC (0) +#define MACHINE_HAS_VX (0) #else /* CONFIG_64BIT */ #define MACHINE_HAS_IEEE (1) #define MACHINE_HAS_CSP (1) @@ -91,8 +91,8 @@ extern void detect_memory_memblock(void); #define MACHINE_HAS_LPP (S390_lowcore.machine_flags & MACHINE_FLAG_LPP) #define MACHINE_HAS_TOPOLOGY (S390_lowcore.machine_flags & MACHINE_FLAG_TOPOLOGY) #define MACHINE_HAS_TE (S390_lowcore.machine_flags & MACHINE_FLAG_TE) -#define MACHINE_HAS_RRBM (S390_lowcore.machine_flags & MACHINE_FLAG_RRBM) #define MACHINE_HAS_TLB_LC (S390_lowcore.machine_flags & MACHINE_FLAG_TLB_LC) +#define MACHINE_HAS_VX (S390_lowcore.machine_flags & MACHINE_FLAG_VX) #endif /* CONFIG_64BIT */ /* diff --git a/arch/s390/include/asm/sigp.h b/arch/s390/include/asm/sigp.h index bf9c823d4020ec2549aecba28996a08d9d69fdc4..49576115dbb76da7659bdcceffca7f1452e37fdf 100644 --- a/arch/s390/include/asm/sigp.h +++ b/arch/s390/include/asm/sigp.h @@ -15,6 +15,7 @@ #define SIGP_SET_ARCHITECTURE 18 #define SIGP_COND_EMERGENCY_SIGNAL 19 #define SIGP_SENSE_RUNNING 21 +#define SIGP_STORE_ADDITIONAL_STATUS 23 /* SIGP condition codes */ #define SIGP_CC_ORDER_CODE_ACCEPTED 0 @@ -33,9 +34,10 @@ #ifndef __ASSEMBLY__ -static inline int __pcpu_sigp(u16 addr, u8 order, u32 parm, u32 *status) +static inline int __pcpu_sigp(u16 addr, u8 order, unsigned long parm, + u32 *status) { - register unsigned int reg1 asm ("1") = parm; + register unsigned long reg1 asm ("1") = parm; int cc; asm volatile( diff --git a/arch/s390/include/asm/smp.h b/arch/s390/include/asm/smp.h index 4f1307962a95a307d6dff37acf897abba8f7595d..762d4f88af5ae3453402e99fd216bd612e2d4377 100644 --- a/arch/s390/include/asm/smp.h +++ b/arch/s390/include/asm/smp.h @@ -29,7 +29,6 @@ extern int smp_find_processor_id(u16 address); extern int smp_store_status(int cpu); extern int smp_vcpu_scheduled(int cpu); extern void smp_yield_cpu(int cpu); -extern void smp_yield(void); extern void smp_cpu_set_polarization(int cpu, int val); extern int smp_cpu_get_polarization(int cpu); extern void smp_fill_possible_mask(void); @@ -50,7 +49,6 @@ static inline int smp_find_processor_id(u16 address) { return 0; } static inline int smp_store_status(int cpu) { return 0; } static inline int smp_vcpu_scheduled(int cpu) { return 1; } static inline void smp_yield_cpu(int cpu) { } -static inline void smp_yield(void) { } static inline void smp_fill_possible_mask(void) { } #endif /* CONFIG_SMP */ diff --git a/arch/s390/include/asm/spinlock.h b/arch/s390/include/asm/spinlock.h index 96879f7ad6daaebd896745a1edf08cd134e8546c..d6bdf906caa533231660113144c74861ef031e6c 100644 --- a/arch/s390/include/asm/spinlock.h +++ b/arch/s390/include/asm/spinlock.h @@ -37,11 +37,17 @@ _raw_compare_and_swap(unsigned int *lock, unsigned int old, unsigned int new) * (the type definitions are in asm/spinlock_types.h) */ +void arch_lock_relax(unsigned int cpu); + void arch_spin_lock_wait(arch_spinlock_t *); int arch_spin_trylock_retry(arch_spinlock_t *); -void arch_spin_relax(arch_spinlock_t *); void arch_spin_lock_wait_flags(arch_spinlock_t *, unsigned long flags); +static inline void arch_spin_relax(arch_spinlock_t *lock) +{ + arch_lock_relax(lock->lock); +} + static inline u32 arch_spin_lockval(int cpu) { return ~cpu; @@ -64,11 +70,6 @@ static inline int arch_spin_trylock_once(arch_spinlock_t *lp) _raw_compare_and_swap(&lp->lock, 0, SPINLOCK_LOCKVAL)); } -static inline int arch_spin_tryrelease_once(arch_spinlock_t *lp) -{ - return _raw_compare_and_swap(&lp->lock, SPINLOCK_LOCKVAL, 0); -} - static inline void arch_spin_lock(arch_spinlock_t *lp) { if (!arch_spin_trylock_once(lp)) @@ -91,7 +92,13 @@ static inline int arch_spin_trylock(arch_spinlock_t *lp) static inline void arch_spin_unlock(arch_spinlock_t *lp) { - arch_spin_tryrelease_once(lp); + typecheck(unsigned int, lp->lock); + asm volatile( + __ASM_BARRIER + "st %1,%0\n" + : "+Q" (lp->lock) + : "d" (0) + : "cc", "memory"); } static inline void arch_spin_unlock_wait(arch_spinlock_t *lock) @@ -123,13 +130,12 @@ static inline void arch_spin_unlock_wait(arch_spinlock_t *lock) */ #define arch_write_can_lock(x) ((x)->lock == 0) -extern void _raw_read_lock_wait(arch_rwlock_t *lp); -extern void _raw_read_lock_wait_flags(arch_rwlock_t *lp, unsigned long flags); extern int _raw_read_trylock_retry(arch_rwlock_t *lp); -extern void _raw_write_lock_wait(arch_rwlock_t *lp); -extern void _raw_write_lock_wait_flags(arch_rwlock_t *lp, unsigned long flags); extern int _raw_write_trylock_retry(arch_rwlock_t *lp); +#define arch_read_lock_flags(lock, flags) arch_read_lock(lock) +#define arch_write_lock_flags(lock, flags) arch_write_lock(lock) + static inline int arch_read_trylock_once(arch_rwlock_t *rw) { unsigned int old = ACCESS_ONCE(rw->lock); @@ -144,16 +150,82 @@ static inline int arch_write_trylock_once(arch_rwlock_t *rw) _raw_compare_and_swap(&rw->lock, 0, 0x80000000)); } +#ifdef CONFIG_HAVE_MARCH_Z196_FEATURES + +#define __RAW_OP_OR "lao" +#define __RAW_OP_AND "lan" +#define __RAW_OP_ADD "laa" + +#define __RAW_LOCK(ptr, op_val, op_string) \ +({ \ + unsigned int old_val; \ + \ + typecheck(unsigned int *, ptr); \ + asm volatile( \ + op_string " %0,%2,%1\n" \ + "bcr 14,0\n" \ + : "=d" (old_val), "+Q" (*ptr) \ + : "d" (op_val) \ + : "cc", "memory"); \ + old_val; \ +}) + +#define __RAW_UNLOCK(ptr, op_val, op_string) \ +({ \ + unsigned int old_val; \ + \ + typecheck(unsigned int *, ptr); \ + asm volatile( \ + "bcr 14,0\n" \ + op_string " %0,%2,%1\n" \ + : "=d" (old_val), "+Q" (*ptr) \ + : "d" (op_val) \ + : "cc", "memory"); \ + old_val; \ +}) + +extern void _raw_read_lock_wait(arch_rwlock_t *lp); +extern void _raw_write_lock_wait(arch_rwlock_t *lp, unsigned int prev); + static inline void arch_read_lock(arch_rwlock_t *rw) { - if (!arch_read_trylock_once(rw)) + unsigned int old; + + old = __RAW_LOCK(&rw->lock, 1, __RAW_OP_ADD); + if ((int) old < 0) _raw_read_lock_wait(rw); } -static inline void arch_read_lock_flags(arch_rwlock_t *rw, unsigned long flags) +static inline void arch_read_unlock(arch_rwlock_t *rw) +{ + __RAW_UNLOCK(&rw->lock, -1, __RAW_OP_ADD); +} + +static inline void arch_write_lock(arch_rwlock_t *rw) +{ + unsigned int old; + + old = __RAW_LOCK(&rw->lock, 0x80000000, __RAW_OP_OR); + if (old != 0) + _raw_write_lock_wait(rw, old); + rw->owner = SPINLOCK_LOCKVAL; +} + +static inline void arch_write_unlock(arch_rwlock_t *rw) +{ + rw->owner = 0; + __RAW_UNLOCK(&rw->lock, 0x7fffffff, __RAW_OP_AND); +} + +#else /* CONFIG_HAVE_MARCH_Z196_FEATURES */ + +extern void _raw_read_lock_wait(arch_rwlock_t *lp); +extern void _raw_write_lock_wait(arch_rwlock_t *lp); + +static inline void arch_read_lock(arch_rwlock_t *rw) { if (!arch_read_trylock_once(rw)) - _raw_read_lock_wait_flags(rw, flags); + _raw_read_lock_wait(rw); } static inline void arch_read_unlock(arch_rwlock_t *rw) @@ -169,19 +241,24 @@ static inline void arch_write_lock(arch_rwlock_t *rw) { if (!arch_write_trylock_once(rw)) _raw_write_lock_wait(rw); -} - -static inline void arch_write_lock_flags(arch_rwlock_t *rw, unsigned long flags) -{ - if (!arch_write_trylock_once(rw)) - _raw_write_lock_wait_flags(rw, flags); + rw->owner = SPINLOCK_LOCKVAL; } static inline void arch_write_unlock(arch_rwlock_t *rw) { - _raw_compare_and_swap(&rw->lock, 0x80000000, 0); + typecheck(unsigned int, rw->lock); + + rw->owner = 0; + asm volatile( + __ASM_BARRIER + "st %1,%0\n" + : "+Q" (rw->lock) + : "d" (0) + : "cc", "memory"); } +#endif /* CONFIG_HAVE_MARCH_Z196_FEATURES */ + static inline int arch_read_trylock(arch_rwlock_t *rw) { if (!arch_read_trylock_once(rw)) @@ -191,12 +268,20 @@ static inline int arch_read_trylock(arch_rwlock_t *rw) static inline int arch_write_trylock(arch_rwlock_t *rw) { - if (!arch_write_trylock_once(rw)) - return _raw_write_trylock_retry(rw); + if (!arch_write_trylock_once(rw) && !_raw_write_trylock_retry(rw)) + return 0; + rw->owner = SPINLOCK_LOCKVAL; return 1; } -#define arch_read_relax(lock) cpu_relax() -#define arch_write_relax(lock) cpu_relax() +static inline void arch_read_relax(arch_rwlock_t *rw) +{ + arch_lock_relax(rw->owner); +} + +static inline void arch_write_relax(arch_rwlock_t *rw) +{ + arch_lock_relax(rw->owner); +} #endif /* __ASM_SPINLOCK_H */ diff --git a/arch/s390/include/asm/spinlock_types.h b/arch/s390/include/asm/spinlock_types.h index b2cd6ff7c2c5bcdad941bb91297fa64fb8908616..d84b6939237c1cc2c4f671edabbe9d062acd7d27 100644 --- a/arch/s390/include/asm/spinlock_types.h +++ b/arch/s390/include/asm/spinlock_types.h @@ -13,6 +13,7 @@ typedef struct { typedef struct { unsigned int lock; + unsigned int owner; } arch_rwlock_t; #define __ARCH_RW_LOCK_UNLOCKED { 0 } diff --git a/arch/s390/include/asm/switch_to.h b/arch/s390/include/asm/switch_to.h index 18ea9e3f8142954926b8fea3afe79ce90193a4b3..2542a7e4c8b4577fa8c1081a0d372ba70e618d38 100644 --- a/arch/s390/include/asm/switch_to.h +++ b/arch/s390/include/asm/switch_to.h @@ -103,6 +103,61 @@ static inline void restore_fp_regs(freg_t *fprs) asm volatile("ld 15,%0" : : "Q" (fprs[15])); } +static inline void save_vx_regs(__vector128 *vxrs) +{ + typedef struct { __vector128 _[__NUM_VXRS]; } addrtype; + + asm volatile( + " la 1,%0\n" + " .word 0xe70f,0x1000,0x003e\n" /* vstm 0,15,0(1) */ + " .word 0xe70f,0x1100,0x0c3e\n" /* vstm 16,31,256(1) */ + : "=Q" (*(addrtype *) vxrs) : : "1"); +} + +static inline void save_vx_regs_safe(__vector128 *vxrs) +{ + unsigned long cr0, flags; + + flags = arch_local_irq_save(); + __ctl_store(cr0, 0, 0); + __ctl_set_bit(0, 17); + __ctl_set_bit(0, 18); + save_vx_regs(vxrs); + __ctl_load(cr0, 0, 0); + arch_local_irq_restore(flags); +} + +static inline void restore_vx_regs(__vector128 *vxrs) +{ + typedef struct { __vector128 _[__NUM_VXRS]; } addrtype; + + asm volatile( + " la 1,%0\n" + " .word 0xe70f,0x1000,0x0036\n" /* vlm 0,15,0(1) */ + " .word 0xe70f,0x1100,0x0c36\n" /* vlm 16,31,256(1) */ + : : "Q" (*(addrtype *) vxrs) : "1"); +} + +static inline void save_fp_vx_regs(struct task_struct *task) +{ +#ifdef CONFIG_64BIT + if (task->thread.vxrs) + save_vx_regs(task->thread.vxrs); + else +#endif + save_fp_regs(task->thread.fp_regs.fprs); +} + +static inline void restore_fp_vx_regs(struct task_struct *task) +{ +#ifdef CONFIG_64BIT + if (task->thread.vxrs) + restore_vx_regs(task->thread.vxrs); + else +#endif + restore_fp_regs(task->thread.fp_regs.fprs); +} + static inline void save_access_regs(unsigned int *acrs) { typedef struct { int _[NUM_ACRS]; } acrstype; @@ -120,16 +175,16 @@ static inline void restore_access_regs(unsigned int *acrs) #define switch_to(prev,next,last) do { \ if (prev->mm) { \ save_fp_ctl(&prev->thread.fp_regs.fpc); \ - save_fp_regs(prev->thread.fp_regs.fprs); \ + save_fp_vx_regs(prev); \ save_access_regs(&prev->thread.acrs[0]); \ save_ri_cb(prev->thread.ri_cb); \ } \ if (next->mm) { \ + update_cr_regs(next); \ restore_fp_ctl(&next->thread.fp_regs.fpc); \ - restore_fp_regs(next->thread.fp_regs.fprs); \ + restore_fp_vx_regs(next); \ restore_access_regs(&next->thread.acrs[0]); \ restore_ri_cb(next->thread.ri_cb, prev->thread.ri_cb); \ - update_cr_regs(next); \ } \ prev = __switch_to(prev,next); \ } while (0) diff --git a/arch/s390/include/asm/thread_info.h b/arch/s390/include/asm/thread_info.h index b833e9c0bfbf2feb6e4f9dc2559e22385115e9b2..4d62fd5b56e5d13a264a1f3b691f90d282c355ac 100644 --- a/arch/s390/include/asm/thread_info.h +++ b/arch/s390/include/asm/thread_info.h @@ -84,11 +84,13 @@ static inline struct thread_info *current_thread_info(void) #define TIF_SYSCALL_AUDIT 4 /* syscall auditing active */ #define TIF_SECCOMP 5 /* secure computing */ #define TIF_SYSCALL_TRACEPOINT 6 /* syscall tracepoint instrumentation */ +#define TIF_UPROBE 7 /* breakpointed or single-stepping */ #define TIF_31BIT 16 /* 32bit process */ #define TIF_MEMDIE 17 /* is terminating due to OOM killer */ #define TIF_RESTORE_SIGMASK 18 /* restore signal mask in do_signal() */ #define TIF_SINGLE_STEP 19 /* This task is single stepped */ #define TIF_BLOCK_STEP 20 /* This task is block stepped */ +#define TIF_UPROBE_SINGLESTEP 21 /* This task is uprobe single stepped */ #define _TIF_NOTIFY_RESUME (1< + +typedef u16 uprobe_opcode_t; + +#define UPROBE_XOL_SLOT_BYTES 256 /* cache aligned */ + +#define UPROBE_SWBP_INSN 0x0002 +#define UPROBE_SWBP_INSN_SIZE 2 + +struct arch_uprobe { + union{ + uprobe_opcode_t insn[3]; + uprobe_opcode_t ixol[3]; + }; + unsigned int saved_per : 1; + unsigned int saved_int_code; +}; + +struct arch_uprobe_task { +}; + +int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, + unsigned long addr); +int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs); +int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs); +bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); +int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, + void *data); +void arch_uprobe_abort_xol(struct arch_uprobe *ap, struct pt_regs *regs); +unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline, + struct pt_regs *regs); +#endif /* _ASM_UPROBES_H */ diff --git a/arch/s390/include/asm/vdso.h b/arch/s390/include/asm/vdso.h index bc9746a7d47c53edf219c4b4b1f500bc0728a284..a62526d09201667f5d485192d9c6d081b7559060 100644 --- a/arch/s390/include/asm/vdso.h +++ b/arch/s390/include/asm/vdso.h @@ -22,13 +22,17 @@ struct vdso_data { __u64 xtime_tod_stamp; /* TOD clock for xtime 0x08 */ __u64 xtime_clock_sec; /* Kernel time 0x10 */ __u64 xtime_clock_nsec; /* 0x18 */ - __u64 wtom_clock_sec; /* Wall to monotonic clock 0x20 */ - __u64 wtom_clock_nsec; /* 0x28 */ - __u32 tz_minuteswest; /* Minutes west of Greenwich 0x30 */ - __u32 tz_dsttime; /* Type of dst correction 0x34 */ - __u32 ectg_available; /* ECTG instruction present 0x38 */ - __u32 tk_mult; /* Mult. used for xtime_nsec 0x3c */ - __u32 tk_shift; /* Shift used for xtime_nsec 0x40 */ + __u64 xtime_coarse_sec; /* Coarse kernel time 0x20 */ + __u64 xtime_coarse_nsec; /* 0x28 */ + __u64 wtom_clock_sec; /* Wall to monotonic clock 0x30 */ + __u64 wtom_clock_nsec; /* 0x38 */ + __u64 wtom_coarse_sec; /* Coarse wall to monotonic 0x40 */ + __u64 wtom_coarse_nsec; /* 0x48 */ + __u32 tz_minuteswest; /* Minutes west of Greenwich 0x50 */ + __u32 tz_dsttime; /* Type of dst correction 0x54 */ + __u32 ectg_available; /* ECTG instruction present 0x58 */ + __u32 tk_mult; /* Mult. used for xtime_nsec 0x5c */ + __u32 tk_shift; /* Shift used for xtime_nsec 0x60 */ }; struct vdso_per_cpu_data { diff --git a/arch/s390/include/asm/vtimer.h b/arch/s390/include/asm/vtimer.h index bfe25d513ad26462f22fce6e66dd3f688e25c352..10a179af62d8c3f512a253752494b61c1801310f 100644 --- a/arch/s390/include/asm/vtimer.h +++ b/arch/s390/include/asm/vtimer.h @@ -28,6 +28,4 @@ extern int del_virt_timer(struct vtimer_list *timer); extern void init_cpu_vtimer(void); extern void vtime_init(void); -extern void vtime_stop_cpu(void); - #endif /* _ASM_S390_TIMER_H */ diff --git a/arch/s390/include/uapi/asm/sigcontext.h b/arch/s390/include/uapi/asm/sigcontext.h index b30de9c01bbedad00c5e15ff52ffa8d19b06c824..5f0b8d7ddb0bce1ad36dd45f70ab757e5a33e99f 100644 --- a/arch/s390/include/uapi/asm/sigcontext.h +++ b/arch/s390/include/uapi/asm/sigcontext.h @@ -7,10 +7,14 @@ #define _ASM_S390_SIGCONTEXT_H #include +#include -#define __NUM_GPRS 16 -#define __NUM_FPRS 16 -#define __NUM_ACRS 16 +#define __NUM_GPRS 16 +#define __NUM_FPRS 16 +#define __NUM_ACRS 16 +#define __NUM_VXRS 32 +#define __NUM_VXRS_LOW 16 +#define __NUM_VXRS_HIGH 16 #ifndef __s390x__ @@ -59,6 +63,16 @@ typedef struct _s390_fp_regs fpregs; } _sigregs; +typedef struct +{ +#ifndef __s390x__ + unsigned long gprs_high[__NUM_GPRS]; +#endif + unsigned long long vxrs_low[__NUM_VXRS_LOW]; + __vector128 vxrs_high[__NUM_VXRS_HIGH]; + unsigned char __reserved[128]; +} _sigregs_ext; + struct sigcontext { unsigned long oldmask[_SIGCONTEXT_NSIG_WORDS]; diff --git a/arch/s390/include/uapi/asm/types.h b/arch/s390/include/uapi/asm/types.h index 038f2b9178a4a2f159d9e289327a074e3ad0704c..3c3951e3415be0efd38b348bda8bea922b17d10a 100644 --- a/arch/s390/include/uapi/asm/types.h +++ b/arch/s390/include/uapi/asm/types.h @@ -17,6 +17,10 @@ typedef unsigned long addr_t; typedef __signed__ long saddr_t; +typedef struct { + __u32 u[4]; +} __vector128; + #endif /* __ASSEMBLY__ */ #endif /* _UAPI_S390_TYPES_H */ diff --git a/arch/s390/include/uapi/asm/ucontext.h b/arch/s390/include/uapi/asm/ucontext.h index 3e077b2a470579af0cb0827c5ba3050fa373450c..64a69aa5dde041cc733265b90aca197044d1fedb 100644 --- a/arch/s390/include/uapi/asm/ucontext.h +++ b/arch/s390/include/uapi/asm/ucontext.h @@ -7,10 +7,15 @@ #ifndef _ASM_S390_UCONTEXT_H #define _ASM_S390_UCONTEXT_H -#define UC_EXTENDED 0x00000001 - -#ifndef __s390x__ +#define UC_GPRS_HIGH 1 /* uc_mcontext_ext has valid high gprs */ +#define UC_VXRS 2 /* uc_mcontext_ext has valid vector regs */ +/* + * The struct ucontext_extended describes how the registers are stored + * on a rt signal frame. Please note that the structure is not fixed, + * if new CPU registers are added to the user state the size of the + * struct ucontext_extended will increase. + */ struct ucontext_extended { unsigned long uc_flags; struct ucontext *uc_link; @@ -19,11 +24,9 @@ struct ucontext_extended { sigset_t uc_sigmask; /* Allow for uc_sigmask growth. Glibc uses a 1024-bit sigset_t. */ unsigned char __unused[128 - sizeof(sigset_t)]; - unsigned long uc_gprs_high[16]; + _sigregs_ext uc_mcontext_ext; }; -#endif - struct ucontext { unsigned long uc_flags; struct ucontext *uc_link; diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index a95c4ca99617e43360fb3b867ac3aefb92b374d6..204c43a4c245dd1835915adbbae39da494c2d947 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -28,7 +28,7 @@ CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"' CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w -obj-y := traps.o time.o process.o base.o early.o setup.o vtime.o +obj-y := traps.o time.o process.o base.o early.o setup.o idle.o vtime.o obj-y += processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o obj-y += debug.o irq.o ipl.o dis.o diag.o sclp.o vdso.o obj-y += sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o pgm_check.o @@ -52,11 +52,9 @@ obj-$(CONFIG_COMPAT) += compat_wrapper.o $(compat-obj-y) obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_KPROBES) += kprobes.o -obj-$(CONFIG_FUNCTION_TRACER) += $(if $(CONFIG_64BIT),mcount64.o,mcount.o) -obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o -obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o -obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o +obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o +obj-$(CONFIG_UPROBES) += uprobes.o ifdef CONFIG_64BIT obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf.o perf_cpum_sf.o \ diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index afe1715a4eb765cb06d1ab3277bce0b4ce8fc3c2..ef279a1368012e67fbbbbb3092783b461e1b98c0 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include @@ -62,8 +62,12 @@ int main(void) DEFINE(__VDSO_XTIME_STAMP, offsetof(struct vdso_data, xtime_tod_stamp)); DEFINE(__VDSO_XTIME_SEC, offsetof(struct vdso_data, xtime_clock_sec)); DEFINE(__VDSO_XTIME_NSEC, offsetof(struct vdso_data, xtime_clock_nsec)); + DEFINE(__VDSO_XTIME_CRS_SEC, offsetof(struct vdso_data, xtime_coarse_sec)); + DEFINE(__VDSO_XTIME_CRS_NSEC, offsetof(struct vdso_data, xtime_coarse_nsec)); DEFINE(__VDSO_WTOM_SEC, offsetof(struct vdso_data, wtom_clock_sec)); DEFINE(__VDSO_WTOM_NSEC, offsetof(struct vdso_data, wtom_clock_nsec)); + DEFINE(__VDSO_WTOM_CRS_SEC, offsetof(struct vdso_data, wtom_coarse_sec)); + DEFINE(__VDSO_WTOM_CRS_NSEC, offsetof(struct vdso_data, wtom_coarse_nsec)); DEFINE(__VDSO_TIMEZONE, offsetof(struct vdso_data, tz_minuteswest)); DEFINE(__VDSO_ECTG_OK, offsetof(struct vdso_data, ectg_available)); DEFINE(__VDSO_TK_MULT, offsetof(struct vdso_data, tk_mult)); @@ -73,8 +77,11 @@ int main(void) /* constants used by the vdso */ DEFINE(__CLOCK_REALTIME, CLOCK_REALTIME); DEFINE(__CLOCK_MONOTONIC, CLOCK_MONOTONIC); + DEFINE(__CLOCK_REALTIME_COARSE, CLOCK_REALTIME_COARSE); + DEFINE(__CLOCK_MONOTONIC_COARSE, CLOCK_MONOTONIC_COARSE); DEFINE(__CLOCK_THREAD_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID); DEFINE(__CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC); + DEFINE(__CLOCK_COARSE_RES, LOW_RES_NSEC); BLANK(); /* idle data offsets */ DEFINE(__CLOCK_IDLE_ENTER, offsetof(struct s390_idle_data, clock_idle_enter)); diff --git a/arch/s390/kernel/compat_linux.h b/arch/s390/kernel/compat_linux.h index 70d4b7c4beaa92d1b85ec7b38373830b38aab3ee..a0a886c0497759e0338acd45157f267d57a724fb 100644 --- a/arch/s390/kernel/compat_linux.h +++ b/arch/s390/kernel/compat_linux.h @@ -50,6 +50,14 @@ typedef struct _s390_fp_regs32 fpregs; } _sigregs32; +typedef struct +{ + __u32 gprs_high[__NUM_GPRS]; + __u64 vxrs_low[__NUM_VXRS_LOW]; + __vector128 vxrs_high[__NUM_VXRS_HIGH]; + __u8 __reserved[128]; +} _sigregs_ext32; + #define _SIGCONTEXT_NSIG32 64 #define _SIGCONTEXT_NSIG_BPW32 32 #define __SIGNAL_FRAMESIZE32 96 @@ -72,6 +80,7 @@ struct ucontext32 { compat_sigset_t uc_sigmask; /* Allow for uc_sigmask growth. Glibc uses a 1024-bit sigset_t. */ unsigned char __unused[128 - sizeof(compat_sigset_t)]; + _sigregs_ext32 uc_mcontext_ext; }; struct stat64_emu31; diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c index 598b0b42668bfc81eb6e2c5293a337e838717bbc..009f5eb111254028125104ccbf25566d2d72c5fa 100644 --- a/arch/s390/kernel/compat_signal.c +++ b/arch/s390/kernel/compat_signal.c @@ -36,17 +36,16 @@ typedef struct struct sigcontext32 sc; _sigregs32 sregs; int signo; - __u32 gprs_high[NUM_GPRS]; - __u8 retcode[S390_SYSCALL_SIZE]; + _sigregs_ext32 sregs_ext; + __u16 svc_insn; /* Offset of svc_insn is NOT fixed! */ } sigframe32; typedef struct { __u8 callee_used_stack[__SIGNAL_FRAMESIZE32]; - __u8 retcode[S390_SYSCALL_SIZE]; + __u16 svc_insn; compat_siginfo_t info; struct ucontext32 uc; - __u32 gprs_high[NUM_GPRS]; } rt_sigframe32; int copy_siginfo_to_user32(compat_siginfo_t __user *to, const siginfo_t *from) @@ -151,6 +150,38 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from) return err ? -EFAULT : 0; } +/* Store registers needed to create the signal frame */ +static void store_sigregs(void) +{ + int i; + + save_access_regs(current->thread.acrs); + save_fp_ctl(¤t->thread.fp_regs.fpc); + if (current->thread.vxrs) { + save_vx_regs(current->thread.vxrs); + for (i = 0; i < __NUM_FPRS; i++) + current->thread.fp_regs.fprs[i] = + *(freg_t *)(current->thread.vxrs + i); + } else + save_fp_regs(current->thread.fp_regs.fprs); +} + +/* Load registers after signal return */ +static void load_sigregs(void) +{ + int i; + + restore_access_regs(current->thread.acrs); + /* restore_fp_ctl is done in restore_sigregs */ + if (current->thread.vxrs) { + for (i = 0; i < __NUM_FPRS; i++) + *(freg_t *)(current->thread.vxrs + i) = + current->thread.fp_regs.fprs[i]; + restore_vx_regs(current->thread.vxrs); + } else + restore_fp_regs(current->thread.fp_regs.fprs); +} + static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs) { _sigregs32 user_sregs; @@ -163,11 +194,8 @@ static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs) (__u32)(regs->psw.mask & PSW_MASK_BA); for (i = 0; i < NUM_GPRS; i++) user_sregs.regs.gprs[i] = (__u32) regs->gprs[i]; - save_access_regs(current->thread.acrs); memcpy(&user_sregs.regs.acrs, current->thread.acrs, sizeof(user_sregs.regs.acrs)); - save_fp_ctl(¤t->thread.fp_regs.fpc); - save_fp_regs(current->thread.fp_regs.fprs); memcpy(&user_sregs.fpregs, ¤t->thread.fp_regs, sizeof(user_sregs.fpregs)); if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs32))) @@ -207,37 +235,67 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs) regs->gprs[i] = (__u64) user_sregs.regs.gprs[i]; memcpy(¤t->thread.acrs, &user_sregs.regs.acrs, sizeof(current->thread.acrs)); - restore_access_regs(current->thread.acrs); memcpy(¤t->thread.fp_regs, &user_sregs.fpregs, sizeof(current->thread.fp_regs)); - restore_fp_regs(current->thread.fp_regs.fprs); clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */ return 0; } -static int save_sigregs_gprs_high(struct pt_regs *regs, __u32 __user *uregs) +static int save_sigregs_ext32(struct pt_regs *regs, + _sigregs_ext32 __user *sregs_ext) { __u32 gprs_high[NUM_GPRS]; + __u64 vxrs[__NUM_VXRS_LOW]; int i; + /* Save high gprs to signal stack */ for (i = 0; i < NUM_GPRS; i++) gprs_high[i] = regs->gprs[i] >> 32; - if (__copy_to_user(uregs, &gprs_high, sizeof(gprs_high))) + if (__copy_to_user(&sregs_ext->gprs_high, &gprs_high, + sizeof(sregs_ext->gprs_high))) return -EFAULT; + + /* Save vector registers to signal stack */ + if (current->thread.vxrs) { + for (i = 0; i < __NUM_VXRS_LOW; i++) + vxrs[i] = *((__u64 *)(current->thread.vxrs + i) + 1); + if (__copy_to_user(&sregs_ext->vxrs_low, vxrs, + sizeof(sregs_ext->vxrs_low)) || + __copy_to_user(&sregs_ext->vxrs_high, + current->thread.vxrs + __NUM_VXRS_LOW, + sizeof(sregs_ext->vxrs_high))) + return -EFAULT; + } return 0; } -static int restore_sigregs_gprs_high(struct pt_regs *regs, __u32 __user *uregs) +static int restore_sigregs_ext32(struct pt_regs *regs, + _sigregs_ext32 __user *sregs_ext) { __u32 gprs_high[NUM_GPRS]; + __u64 vxrs[__NUM_VXRS_LOW]; int i; - if (__copy_from_user(&gprs_high, uregs, sizeof(gprs_high))) + /* Restore high gprs from signal stack */ + if (__copy_from_user(&gprs_high, &sregs_ext->gprs_high, + sizeof(&sregs_ext->gprs_high))) return -EFAULT; for (i = 0; i < NUM_GPRS; i++) *(__u32 *)®s->gprs[i] = gprs_high[i]; + + /* Restore vector registers from signal stack */ + if (current->thread.vxrs) { + if (__copy_from_user(vxrs, &sregs_ext->vxrs_low, + sizeof(sregs_ext->vxrs_low)) || + __copy_from_user(current->thread.vxrs + __NUM_VXRS_LOW, + &sregs_ext->vxrs_high, + sizeof(sregs_ext->vxrs_high))) + return -EFAULT; + for (i = 0; i < __NUM_VXRS_LOW; i++) + *((__u64 *)(current->thread.vxrs + i) + 1) = vxrs[i]; + } return 0; } @@ -252,8 +310,9 @@ COMPAT_SYSCALL_DEFINE0(sigreturn) set_current_blocked(&set); if (restore_sigregs32(regs, &frame->sregs)) goto badframe; - if (restore_sigregs_gprs_high(regs, frame->gprs_high)) + if (restore_sigregs_ext32(regs, &frame->sregs_ext)) goto badframe; + load_sigregs(); return regs->gprs[2]; badframe: force_sig(SIGSEGV, current); @@ -269,12 +328,13 @@ COMPAT_SYSCALL_DEFINE0(rt_sigreturn) if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) goto badframe; set_current_blocked(&set); + if (compat_restore_altstack(&frame->uc.uc_stack)) + goto badframe; if (restore_sigregs32(regs, &frame->uc.uc_mcontext)) goto badframe; - if (restore_sigregs_gprs_high(regs, frame->gprs_high)) + if (restore_sigregs_ext32(regs, &frame->uc.uc_mcontext_ext)) goto badframe; - if (compat_restore_altstack(&frame->uc.uc_stack)) - goto badframe; + load_sigregs(); return regs->gprs[2]; badframe: force_sig(SIGSEGV, current); @@ -324,37 +384,64 @@ static int setup_frame32(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { int sig = ksig->sig; - sigframe32 __user *frame = get_sigframe(&ksig->ka, regs, sizeof(sigframe32)); - + sigframe32 __user *frame; + struct sigcontext32 sc; + unsigned long restorer; + size_t frame_size; + + /* + * gprs_high are always present for 31-bit compat tasks. + * The space for vector registers is only allocated if + * the machine supports it + */ + frame_size = sizeof(*frame) - sizeof(frame->sregs_ext.__reserved); + if (!MACHINE_HAS_VX) + frame_size -= sizeof(frame->sregs_ext.vxrs_low) + + sizeof(frame->sregs_ext.vxrs_high); + frame = get_sigframe(&ksig->ka, regs, frame_size); if (frame == (void __user *) -1UL) return -EFAULT; - if (__copy_to_user(&frame->sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE32)) + /* Set up backchain. */ + if (__put_user(regs->gprs[15], (unsigned int __user *) frame)) + return -EFAULT; + + /* Create struct sigcontext32 on the signal stack */ + memcpy(&sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE32); + sc.sregs = (__u32)(unsigned long __force) &frame->sregs; + if (__copy_to_user(&frame->sc, &sc, sizeof(frame->sc))) return -EFAULT; + /* Store registers needed to create the signal frame */ + store_sigregs(); + + /* Create _sigregs32 on the signal stack */ if (save_sigregs32(regs, &frame->sregs)) return -EFAULT; - if (save_sigregs_gprs_high(regs, frame->gprs_high)) + + /* Place signal number on stack to allow backtrace from handler. */ + if (__put_user(regs->gprs[2], (int __force __user *) &frame->signo)) return -EFAULT; - if (__put_user((unsigned long) &frame->sregs, &frame->sc.sregs)) + + /* Create _sigregs_ext32 on the signal stack */ + if (save_sigregs_ext32(regs, &frame->sregs_ext)) return -EFAULT; /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ksig->ka.sa.sa_flags & SA_RESTORER) { - regs->gprs[14] = (__u64 __force) ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE; + restorer = (unsigned long __force) + ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE; } else { - regs->gprs[14] = (__u64 __force) frame->retcode | PSW32_ADDR_AMODE; - if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, - (u16 __force __user *)(frame->retcode))) + /* Signal frames without vectors registers are short ! */ + __u16 __user *svc = (void *) frame + frame_size - 2; + if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, svc)) return -EFAULT; + restorer = (unsigned long __force) svc | PSW32_ADDR_AMODE; } - /* Set up backchain. */ - if (__put_user(regs->gprs[15], (unsigned int __user *) frame)) - return -EFAULT; - /* Set up registers for signal handler */ + regs->gprs[14] = restorer; regs->gprs[15] = (__force __u64) frame; /* Force 31 bit amode and default user address space control. */ regs->psw.mask = PSW_MASK_BA | @@ -375,50 +462,69 @@ static int setup_frame32(struct ksignal *ksig, sigset_t *set, regs->gprs[6] = task_thread_info(current)->last_break; } - /* Place signal number on stack to allow backtrace from handler. */ - if (__put_user(regs->gprs[2], (int __force __user *) &frame->signo)) - return -EFAULT; return 0; } static int setup_rt_frame32(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { - int err = 0; - rt_sigframe32 __user *frame = get_sigframe(&ksig->ka, regs, sizeof(rt_sigframe32)); - + rt_sigframe32 __user *frame; + unsigned long restorer; + size_t frame_size; + u32 uc_flags; + + frame_size = sizeof(*frame) - + sizeof(frame->uc.uc_mcontext_ext.__reserved); + /* + * gprs_high are always present for 31-bit compat tasks. + * The space for vector registers is only allocated if + * the machine supports it + */ + uc_flags = UC_GPRS_HIGH; + if (MACHINE_HAS_VX) { + if (current->thread.vxrs) + uc_flags |= UC_VXRS; + } else + frame_size -= sizeof(frame->uc.uc_mcontext_ext.vxrs_low) + + sizeof(frame->uc.uc_mcontext_ext.vxrs_high); + frame = get_sigframe(&ksig->ka, regs, frame_size); if (frame == (void __user *) -1UL) return -EFAULT; - if (copy_siginfo_to_user32(&frame->info, &ksig->info)) - return -EFAULT; - - /* Create the ucontext. */ - err |= __put_user(UC_EXTENDED, &frame->uc.uc_flags); - err |= __put_user(0, &frame->uc.uc_link); - err |= __compat_save_altstack(&frame->uc.uc_stack, regs->gprs[15]); - err |= save_sigregs32(regs, &frame->uc.uc_mcontext); - err |= save_sigregs_gprs_high(regs, frame->gprs_high); - err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); - if (err) + /* Set up backchain. */ + if (__put_user(regs->gprs[15], (unsigned int __force __user *) frame)) return -EFAULT; /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ksig->ka.sa.sa_flags & SA_RESTORER) { - regs->gprs[14] = (__u64 __force) ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE; + restorer = (unsigned long __force) + ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE; } else { - regs->gprs[14] = (__u64 __force) frame->retcode | PSW32_ADDR_AMODE; - if (__put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, - (u16 __force __user *)(frame->retcode))) + __u16 __user *svc = &frame->svc_insn; + if (__put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, svc)) return -EFAULT; + restorer = (unsigned long __force) svc | PSW32_ADDR_AMODE; } - /* Set up backchain. */ - if (__put_user(regs->gprs[15], (unsigned int __force __user *) frame)) + /* Create siginfo on the signal stack */ + if (copy_siginfo_to_user32(&frame->info, &ksig->info)) + return -EFAULT; + + /* Store registers needed to create the signal frame */ + store_sigregs(); + + /* Create ucontext on the signal stack. */ + if (__put_user(uc_flags, &frame->uc.uc_flags) || + __put_user(0, &frame->uc.uc_link) || + __compat_save_altstack(&frame->uc.uc_stack, regs->gprs[15]) || + save_sigregs32(regs, &frame->uc.uc_mcontext) || + __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)) || + save_sigregs_ext32(regs, &frame->uc.uc_mcontext_ext)) return -EFAULT; /* Set up registers for signal handler */ + regs->gprs[14] = restorer; regs->gprs[15] = (__force __u64) frame; /* Force 31 bit amode and default user address space control. */ regs->psw.mask = PSW_MASK_BA | diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c index a3b9150e6802973b3b82a6db379c7959dfb48e9d..9f73c805902291c1619335cb38c4fe0f8492b29a 100644 --- a/arch/s390/kernel/crash_dump.c +++ b/arch/s390/kernel/crash_dump.c @@ -46,9 +46,9 @@ struct dump_save_areas dump_save_areas; /* * Allocate and add a save area for a CPU */ -struct save_area *dump_save_area_create(int cpu) +struct save_area_ext *dump_save_area_create(int cpu) { - struct save_area **save_areas, *save_area; + struct save_area_ext **save_areas, *save_area; save_area = kmalloc(sizeof(*save_area), GFP_KERNEL); if (!save_area) @@ -385,10 +385,46 @@ static void *nt_s390_prefix(void *ptr, struct save_area *sa) sizeof(sa->pref_reg), KEXEC_CORE_NOTE_NAME); } +/* + * Initialize vxrs high note (full 128 bit VX registers 16-31) + */ +static void *nt_s390_vx_high(void *ptr, __vector128 *vx_regs) +{ + return nt_init(ptr, NT_S390_VXRS_HIGH, &vx_regs[16], + 16 * sizeof(__vector128), KEXEC_CORE_NOTE_NAME); +} + +/* + * Initialize vxrs low note (lower halves of VX registers 0-15) + */ +static void *nt_s390_vx_low(void *ptr, __vector128 *vx_regs) +{ + Elf64_Nhdr *note; + u64 len; + int i; + + note = (Elf64_Nhdr *)ptr; + note->n_namesz = strlen(KEXEC_CORE_NOTE_NAME) + 1; + note->n_descsz = 16 * 8; + note->n_type = NT_S390_VXRS_LOW; + len = sizeof(Elf64_Nhdr); + + memcpy(ptr + len, KEXEC_CORE_NOTE_NAME, note->n_namesz); + len = roundup(len + note->n_namesz, 4); + + ptr += len; + /* Copy lower halves of SIMD registers 0-15 */ + for (i = 0; i < 16; i++) { + memcpy(ptr, &vx_regs[i], 8); + ptr += 8; + } + return ptr; +} + /* * Fill ELF notes for one CPU with save area registers */ -void *fill_cpu_elf_notes(void *ptr, struct save_area *sa) +void *fill_cpu_elf_notes(void *ptr, struct save_area *sa, __vector128 *vx_regs) { ptr = nt_prstatus(ptr, sa); ptr = nt_fpregset(ptr, sa); @@ -397,6 +433,10 @@ void *fill_cpu_elf_notes(void *ptr, struct save_area *sa) ptr = nt_s390_tod_preg(ptr, sa); ptr = nt_s390_ctrs(ptr, sa); ptr = nt_s390_prefix(ptr, sa); + if (MACHINE_HAS_VX && vx_regs) { + ptr = nt_s390_vx_low(ptr, vx_regs); + ptr = nt_s390_vx_high(ptr, vx_regs); + } return ptr; } @@ -484,7 +524,7 @@ static int get_cpu_cnt(void) int i, cpus = 0; for (i = 0; i < dump_save_areas.count; i++) { - if (dump_save_areas.areas[i]->pref_reg == 0) + if (dump_save_areas.areas[i]->sa.pref_reg == 0) continue; cpus++; } @@ -530,17 +570,17 @@ static void loads_init(Elf64_Phdr *phdr, u64 loads_offset) */ static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset) { - struct save_area *sa; + struct save_area_ext *sa_ext; void *ptr_start = ptr; int i; ptr = nt_prpsinfo(ptr); for (i = 0; i < dump_save_areas.count; i++) { - sa = dump_save_areas.areas[i]; - if (sa->pref_reg == 0) + sa_ext = dump_save_areas.areas[i]; + if (sa_ext->sa.pref_reg == 0) continue; - ptr = fill_cpu_elf_notes(ptr, sa); + ptr = fill_cpu_elf_notes(ptr, &sa_ext->sa, sa_ext->vx_regs); } ptr = nt_vmcoreinfo(ptr); memset(phdr, 0, sizeof(*phdr)); @@ -581,7 +621,7 @@ int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size) mem_chunk_cnt = get_mem_chunk_cnt(); - alloc_size = 0x1000 + get_cpu_cnt() * 0x300 + + alloc_size = 0x1000 + get_cpu_cnt() * 0x4a0 + mem_chunk_cnt * sizeof(Elf64_Phdr); hdr = kzalloc_panic(alloc_size); /* Init elf header */ diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c index 993efe6a887c2c31d4bcd90b02bccbb3c0234ae9..f3762937dd82ee8afcc2c0fddae6397e2be863fa 100644 --- a/arch/s390/kernel/dis.c +++ b/arch/s390/kernel/dis.c @@ -60,6 +60,11 @@ enum { A_28, /* Access reg. starting at position 28 */ C_8, /* Control reg. starting at position 8 */ C_12, /* Control reg. starting at position 12 */ + V_8, /* Vector reg. starting at position 8, extension bit at 36 */ + V_12, /* Vector reg. starting at position 12, extension bit at 37 */ + V_16, /* Vector reg. starting at position 16, extension bit at 38 */ + V_32, /* Vector reg. starting at position 32, extension bit at 39 */ + W_12, /* Vector reg. at bit 12, extension at bit 37, used as index */ B_16, /* Base register starting at position 16 */ B_32, /* Base register starting at position 32 */ X_12, /* Index register starting at position 12 */ @@ -82,6 +87,8 @@ enum { U8_24, /* 8 bit unsigned value starting at 24 */ U8_32, /* 8 bit unsigned value starting at 32 */ I8_8, /* 8 bit signed value starting at 8 */ + I8_16, /* 8 bit signed value starting at 16 */ + I8_24, /* 8 bit signed value starting at 24 */ I8_32, /* 8 bit signed value starting at 32 */ J12_12, /* PC relative offset at 12 */ I16_16, /* 16 bit signed value starting at 16 */ @@ -96,6 +103,9 @@ enum { U32_16, /* 32 bit unsigned value starting at 16 */ M_16, /* 4 bit optional mask starting at 16 */ M_20, /* 4 bit optional mask starting at 20 */ + M_24, /* 4 bit optional mask starting at 24 */ + M_28, /* 4 bit optional mask starting at 28 */ + M_32, /* 4 bit optional mask starting at 32 */ RO_28, /* optional GPR starting at position 28 */ }; @@ -130,7 +140,7 @@ enum { INSTR_RSY_RDRM, INSTR_RS_AARD, INSTR_RS_CCRD, INSTR_RS_R0RD, INSTR_RS_RRRD, INSTR_RS_RURD, - INSTR_RXE_FRRD, INSTR_RXE_RRRD, + INSTR_RXE_FRRD, INSTR_RXE_RRRD, INSTR_RXE_RRRDM, INSTR_RXF_FRRDF, INSTR_RXY_FRRD, INSTR_RXY_RRRD, INSTR_RXY_URRD, INSTR_RX_FRRD, INSTR_RX_RRRD, INSTR_RX_URRD, @@ -143,6 +153,17 @@ enum { INSTR_SS_L0RDRD, INSTR_SS_LIRDRD, INSTR_SS_LLRDRD, INSTR_SS_RRRDRD, INSTR_SS_RRRDRD2, INSTR_SS_RRRDRD3, INSTR_S_00, INSTR_S_RD, + INSTR_VRI_V0IM, INSTR_VRI_V0I0, INSTR_VRI_V0IIM, INSTR_VRI_VVIM, + INSTR_VRI_VVV0IM, INSTR_VRI_VVV0I0, INSTR_VRI_VVIMM, + INSTR_VRR_VV00MMM, INSTR_VRR_VV000MM, INSTR_VRR_VV0000M, + INSTR_VRR_VV00000, INSTR_VRR_VVV0M0M, INSTR_VRR_VV00M0M, + INSTR_VRR_VVV000M, INSTR_VRR_VVV000V, INSTR_VRR_VVV0000, + INSTR_VRR_VVV0MMM, INSTR_VRR_VVV00MM, INSTR_VRR_VVVMM0V, + INSTR_VRR_VVVM0MV, INSTR_VRR_VVVM00V, INSTR_VRR_VRR0000, + INSTR_VRS_VVRDM, INSTR_VRS_VVRD0, INSTR_VRS_VRRDM, INSTR_VRS_VRRD0, + INSTR_VRS_RVRDM, + INSTR_VRV_VVRDM, INSTR_VRV_VWRDM, + INSTR_VRX_VRRDM, INSTR_VRX_VRRD0, }; static const struct s390_operand operands[] = @@ -168,6 +189,11 @@ static const struct s390_operand operands[] = [A_28] = { 4, 28, OPERAND_AR }, [C_8] = { 4, 8, OPERAND_CR }, [C_12] = { 4, 12, OPERAND_CR }, + [V_8] = { 4, 8, OPERAND_VR }, + [V_12] = { 4, 12, OPERAND_VR }, + [V_16] = { 4, 16, OPERAND_VR }, + [V_32] = { 4, 32, OPERAND_VR }, + [W_12] = { 4, 12, OPERAND_INDEX | OPERAND_VR }, [B_16] = { 4, 16, OPERAND_BASE | OPERAND_GPR }, [B_32] = { 4, 32, OPERAND_BASE | OPERAND_GPR }, [X_12] = { 4, 12, OPERAND_INDEX | OPERAND_GPR }, @@ -190,6 +216,11 @@ static const struct s390_operand operands[] = [U8_24] = { 8, 24, 0 }, [U8_32] = { 8, 32, 0 }, [J12_12] = { 12, 12, OPERAND_PCREL }, + [I8_8] = { 8, 8, OPERAND_SIGNED }, + [I8_16] = { 8, 16, OPERAND_SIGNED }, + [I8_24] = { 8, 24, OPERAND_SIGNED }, + [I8_32] = { 8, 32, OPERAND_SIGNED }, + [I16_32] = { 16, 32, OPERAND_SIGNED }, [I16_16] = { 16, 16, OPERAND_SIGNED }, [U16_16] = { 16, 16, 0 }, [U16_32] = { 16, 32, 0 }, @@ -202,6 +233,9 @@ static const struct s390_operand operands[] = [U32_16] = { 32, 16, 0 }, [M_16] = { 4, 16, 0 }, [M_20] = { 4, 20, 0 }, + [M_24] = { 4, 24, 0 }, + [M_28] = { 4, 28, 0 }, + [M_32] = { 4, 32, 0 }, [RO_28] = { 4, 28, OPERAND_GPR } }; @@ -283,6 +317,7 @@ static const unsigned char formats[][7] = { [INSTR_RS_RURD] = { 0xff, R_8,U4_12,D_20,B_16,0,0 }, [INSTR_RXE_FRRD] = { 0xff, F_8,D_20,X_12,B_16,0,0 }, [INSTR_RXE_RRRD] = { 0xff, R_8,D_20,X_12,B_16,0,0 }, + [INSTR_RXE_RRRDM] = { 0xff, R_8,D_20,X_12,B_16,M_32,0 }, [INSTR_RXF_FRRDF] = { 0xff, F_32,F_8,D_20,X_12,B_16,0 }, [INSTR_RXY_FRRD] = { 0xff, F_8,D20_20,X_12,B_16,0,0 }, [INSTR_RXY_RRRD] = { 0xff, R_8,D20_20,X_12,B_16,0,0 }, @@ -307,6 +342,37 @@ static const unsigned char formats[][7] = { [INSTR_SS_RRRDRD] = { 0xff, D_20,R_8,B_16,D_36,B_32,R_12 }, [INSTR_S_00] = { 0xff, 0,0,0,0,0,0 }, [INSTR_S_RD] = { 0xff, D_20,B_16,0,0,0,0 }, + [INSTR_VRI_V0IM] = { 0xff, V_8,I16_16,M_32,0,0,0 }, + [INSTR_VRI_V0I0] = { 0xff, V_8,I16_16,0,0,0,0 }, + [INSTR_VRI_V0IIM] = { 0xff, V_8,I8_16,I8_24,M_32,0,0 }, + [INSTR_VRI_VVIM] = { 0xff, V_8,I16_16,V_12,M_32,0,0 }, + [INSTR_VRI_VVV0IM]= { 0xff, V_8,V_12,V_16,I8_24,M_32,0 }, + [INSTR_VRI_VVV0I0]= { 0xff, V_8,V_12,V_16,I8_24,0,0 }, + [INSTR_VRI_VVIMM] = { 0xff, V_8,V_12,I16_16,M_32,M_28,0 }, + [INSTR_VRR_VV00MMM]={ 0xff, V_8,V_12,M_32,M_28,M_24,0 }, + [INSTR_VRR_VV000MM]={ 0xff, V_8,V_12,M_32,M_28,0,0 }, + [INSTR_VRR_VV0000M]={ 0xff, V_8,V_12,M_32,0,0,0 }, + [INSTR_VRR_VV00000]={ 0xff, V_8,V_12,0,0,0,0 }, + [INSTR_VRR_VVV0M0M]={ 0xff, V_8,V_12,V_16,M_32,M_24,0 }, + [INSTR_VRR_VV00M0M]={ 0xff, V_8,V_12,M_32,M_24,0,0 }, + [INSTR_VRR_VVV000M]={ 0xff, V_8,V_12,V_16,M_32,0,0 }, + [INSTR_VRR_VVV000V]={ 0xff, V_8,V_12,V_16,V_32,0,0 }, + [INSTR_VRR_VVV0000]={ 0xff, V_8,V_12,V_16,0,0,0 }, + [INSTR_VRR_VVV0MMM]={ 0xff, V_8,V_12,V_16,M_32,M_28,M_24 }, + [INSTR_VRR_VVV00MM]={ 0xff, V_8,V_12,V_16,M_32,M_28,0 }, + [INSTR_VRR_VVVMM0V]={ 0xff, V_8,V_12,V_16,V_32,M_20,M_24 }, + [INSTR_VRR_VVVM0MV]={ 0xff, V_8,V_12,V_16,V_32,M_28,M_20 }, + [INSTR_VRR_VVVM00V]={ 0xff, V_8,V_12,V_16,V_32,M_20,0 }, + [INSTR_VRR_VRR0000]={ 0xff, V_8,R_12,R_16,0,0,0 }, + [INSTR_VRS_VVRDM] = { 0xff, V_8,V_12,D_20,B_16,M_32,0 }, + [INSTR_VRS_VVRD0] = { 0xff, V_8,V_12,D_20,B_16,0,0 }, + [INSTR_VRS_VRRDM] = { 0xff, V_8,R_12,D_20,B_16,M_32,0 }, + [INSTR_VRS_VRRD0] = { 0xff, V_8,R_12,D_20,B_16,0,0 }, + [INSTR_VRS_RVRDM] = { 0xff, R_8,V_12,D_20,B_16,M_32,0 }, + [INSTR_VRV_VVRDM] = { 0xff, V_8,V_12,D_20,B_16,M_32,0 }, + [INSTR_VRV_VWRDM] = { 0xff, V_8,D_20,W_12,B_16,M_32,0 }, + [INSTR_VRX_VRRDM] = { 0xff, V_8,D_20,X_12,B_16,M_32,0 }, + [INSTR_VRX_VRRD0] = { 0xff, V_8,D_20,X_12,B_16,0,0 }, }; enum { @@ -381,6 +447,11 @@ enum { LONG_INSN_MPCIFC, LONG_INSN_STPCIFC, LONG_INSN_PCISTB, + LONG_INSN_VPOPCT, + LONG_INSN_VERLLV, + LONG_INSN_VESRAV, + LONG_INSN_VESRLV, + LONG_INSN_VSBCBI }; static char *long_insn_name[] = { @@ -455,6 +526,11 @@ static char *long_insn_name[] = { [LONG_INSN_MPCIFC] = "mpcifc", [LONG_INSN_STPCIFC] = "stpcifc", [LONG_INSN_PCISTB] = "pcistb", + [LONG_INSN_VPOPCT] = "vpopct", + [LONG_INSN_VERLLV] = "verllv", + [LONG_INSN_VESRAV] = "vesrav", + [LONG_INSN_VESRLV] = "vesrlv", + [LONG_INSN_VSBCBI] = "vsbcbi", }; static struct s390_insn opcode[] = { @@ -1369,6 +1445,150 @@ static struct s390_insn opcode_e5[] = { { "", 0, INSTR_INVALID } }; +static struct s390_insn opcode_e7[] = { +#ifdef CONFIG_64BIT + { "lcbb", 0x27, INSTR_RXE_RRRDM }, + { "vgef", 0x13, INSTR_VRV_VVRDM }, + { "vgeg", 0x12, INSTR_VRV_VVRDM }, + { "vgbm", 0x44, INSTR_VRI_V0I0 }, + { "vgm", 0x46, INSTR_VRI_V0IIM }, + { "vl", 0x06, INSTR_VRX_VRRD0 }, + { "vlr", 0x56, INSTR_VRR_VV00000 }, + { "vlrp", 0x05, INSTR_VRX_VRRDM }, + { "vleb", 0x00, INSTR_VRX_VRRDM }, + { "vleh", 0x01, INSTR_VRX_VRRDM }, + { "vlef", 0x03, INSTR_VRX_VRRDM }, + { "vleg", 0x02, INSTR_VRX_VRRDM }, + { "vleib", 0x40, INSTR_VRI_V0IM }, + { "vleih", 0x41, INSTR_VRI_V0IM }, + { "vleif", 0x43, INSTR_VRI_V0IM }, + { "vleig", 0x42, INSTR_VRI_V0IM }, + { "vlgv", 0x21, INSTR_VRS_RVRDM }, + { "vllez", 0x04, INSTR_VRX_VRRDM }, + { "vlm", 0x36, INSTR_VRS_VVRD0 }, + { "vlbb", 0x07, INSTR_VRX_VRRDM }, + { "vlvg", 0x22, INSTR_VRS_VRRDM }, + { "vlvgp", 0x62, INSTR_VRR_VRR0000 }, + { "vll", 0x37, INSTR_VRS_VRRD0 }, + { "vmrh", 0x61, INSTR_VRR_VVV000M }, + { "vmrl", 0x60, INSTR_VRR_VVV000M }, + { "vpk", 0x94, INSTR_VRR_VVV000M }, + { "vpks", 0x97, INSTR_VRR_VVV0M0M }, + { "vpkls", 0x95, INSTR_VRR_VVV0M0M }, + { "vperm", 0x8c, INSTR_VRR_VVV000V }, + { "vpdi", 0x84, INSTR_VRR_VVV000M }, + { "vrep", 0x4d, INSTR_VRI_VVIM }, + { "vrepi", 0x45, INSTR_VRI_V0IM }, + { "vscef", 0x1b, INSTR_VRV_VWRDM }, + { "vsceg", 0x1a, INSTR_VRV_VWRDM }, + { "vsel", 0x8d, INSTR_VRR_VVV000V }, + { "vseg", 0x5f, INSTR_VRR_VV0000M }, + { "vst", 0x0e, INSTR_VRX_VRRD0 }, + { "vsteb", 0x08, INSTR_VRX_VRRDM }, + { "vsteh", 0x09, INSTR_VRX_VRRDM }, + { "vstef", 0x0b, INSTR_VRX_VRRDM }, + { "vsteg", 0x0a, INSTR_VRX_VRRDM }, + { "vstm", 0x3e, INSTR_VRS_VVRD0 }, + { "vstl", 0x3f, INSTR_VRS_VRRD0 }, + { "vuph", 0xd7, INSTR_VRR_VV0000M }, + { "vuplh", 0xd5, INSTR_VRR_VV0000M }, + { "vupl", 0xd6, INSTR_VRR_VV0000M }, + { "vupll", 0xd4, INSTR_VRR_VV0000M }, + { "va", 0xf3, INSTR_VRR_VVV000M }, + { "vacc", 0xf1, INSTR_VRR_VVV000M }, + { "vac", 0xbb, INSTR_VRR_VVVM00V }, + { "vaccc", 0xb9, INSTR_VRR_VVVM00V }, + { "vn", 0x68, INSTR_VRR_VVV0000 }, + { "vnc", 0x69, INSTR_VRR_VVV0000 }, + { "vavg", 0xf2, INSTR_VRR_VVV000M }, + { "vavgl", 0xf0, INSTR_VRR_VVV000M }, + { "vcksm", 0x66, INSTR_VRR_VVV0000 }, + { "vec", 0xdb, INSTR_VRR_VV0000M }, + { "vecl", 0xd9, INSTR_VRR_VV0000M }, + { "vceq", 0xf8, INSTR_VRR_VVV0M0M }, + { "vch", 0xfb, INSTR_VRR_VVV0M0M }, + { "vchl", 0xf9, INSTR_VRR_VVV0M0M }, + { "vclz", 0x53, INSTR_VRR_VV0000M }, + { "vctz", 0x52, INSTR_VRR_VV0000M }, + { "vx", 0x6d, INSTR_VRR_VVV0000 }, + { "vgfm", 0xb4, INSTR_VRR_VVV000M }, + { "vgfma", 0xbc, INSTR_VRR_VVVM00V }, + { "vlc", 0xde, INSTR_VRR_VV0000M }, + { "vlp", 0xdf, INSTR_VRR_VV0000M }, + { "vmx", 0xff, INSTR_VRR_VVV000M }, + { "vmxl", 0xfd, INSTR_VRR_VVV000M }, + { "vmn", 0xfe, INSTR_VRR_VVV000M }, + { "vmnl", 0xfc, INSTR_VRR_VVV000M }, + { "vmal", 0xaa, INSTR_VRR_VVVM00V }, + { "vmae", 0xae, INSTR_VRR_VVVM00V }, + { "vmale", 0xac, INSTR_VRR_VVVM00V }, + { "vmah", 0xab, INSTR_VRR_VVVM00V }, + { "vmalh", 0xa9, INSTR_VRR_VVVM00V }, + { "vmao", 0xaf, INSTR_VRR_VVVM00V }, + { "vmalo", 0xad, INSTR_VRR_VVVM00V }, + { "vmh", 0xa3, INSTR_VRR_VVV000M }, + { "vmlh", 0xa1, INSTR_VRR_VVV000M }, + { "vml", 0xa2, INSTR_VRR_VVV000M }, + { "vme", 0xa6, INSTR_VRR_VVV000M }, + { "vmle", 0xa4, INSTR_VRR_VVV000M }, + { "vmo", 0xa7, INSTR_VRR_VVV000M }, + { "vmlo", 0xa5, INSTR_VRR_VVV000M }, + { "vno", 0x6b, INSTR_VRR_VVV0000 }, + { "vo", 0x6a, INSTR_VRR_VVV0000 }, + { { 0, LONG_INSN_VPOPCT }, 0x50, INSTR_VRR_VV0000M }, + { { 0, LONG_INSN_VERLLV }, 0x73, INSTR_VRR_VVV000M }, + { "verll", 0x33, INSTR_VRS_VVRDM }, + { "verim", 0x72, INSTR_VRI_VVV0IM }, + { "veslv", 0x70, INSTR_VRR_VVV000M }, + { "vesl", 0x30, INSTR_VRS_VVRDM }, + { { 0, LONG_INSN_VESRAV }, 0x7a, INSTR_VRR_VVV000M }, + { "vesra", 0x3a, INSTR_VRS_VVRDM }, + { { 0, LONG_INSN_VESRLV }, 0x78, INSTR_VRR_VVV000M }, + { "vesrl", 0x38, INSTR_VRS_VVRDM }, + { "vsl", 0x74, INSTR_VRR_VVV0000 }, + { "vslb", 0x75, INSTR_VRR_VVV0000 }, + { "vsldb", 0x77, INSTR_VRI_VVV0I0 }, + { "vsra", 0x7e, INSTR_VRR_VVV0000 }, + { "vsrab", 0x7f, INSTR_VRR_VVV0000 }, + { "vsrl", 0x7c, INSTR_VRR_VVV0000 }, + { "vsrlb", 0x7d, INSTR_VRR_VVV0000 }, + { "vs", 0xf7, INSTR_VRR_VVV000M }, + { "vscb", 0xf5, INSTR_VRR_VVV000M }, + { "vsb", 0xbf, INSTR_VRR_VVVM00V }, + { { 0, LONG_INSN_VSBCBI }, 0xbd, INSTR_VRR_VVVM00V }, + { "vsumg", 0x65, INSTR_VRR_VVV000M }, + { "vsumq", 0x67, INSTR_VRR_VVV000M }, + { "vsum", 0x64, INSTR_VRR_VVV000M }, + { "vtm", 0xd8, INSTR_VRR_VV00000 }, + { "vfae", 0x82, INSTR_VRR_VVV0M0M }, + { "vfee", 0x80, INSTR_VRR_VVV0M0M }, + { "vfene", 0x81, INSTR_VRR_VVV0M0M }, + { "vistr", 0x5c, INSTR_VRR_VV00M0M }, + { "vstrc", 0x8a, INSTR_VRR_VVVMM0V }, + { "vfa", 0xe3, INSTR_VRR_VVV00MM }, + { "wfc", 0xcb, INSTR_VRR_VV000MM }, + { "wfk", 0xca, INSTR_VRR_VV000MM }, + { "vfce", 0xe8, INSTR_VRR_VVV0MMM }, + { "vfch", 0xeb, INSTR_VRR_VVV0MMM }, + { "vfche", 0xea, INSTR_VRR_VVV0MMM }, + { "vcdg", 0xc3, INSTR_VRR_VV00MMM }, + { "vcdlg", 0xc1, INSTR_VRR_VV00MMM }, + { "vcgd", 0xc2, INSTR_VRR_VV00MMM }, + { "vclgd", 0xc0, INSTR_VRR_VV00MMM }, + { "vfd", 0xe5, INSTR_VRR_VVV00MM }, + { "vfi", 0xc7, INSTR_VRR_VV00MMM }, + { "vlde", 0xc4, INSTR_VRR_VV000MM }, + { "vled", 0xc5, INSTR_VRR_VV00MMM }, + { "vfm", 0xe7, INSTR_VRR_VVV00MM }, + { "vfma", 0x8f, INSTR_VRR_VVVM0MV }, + { "vfms", 0x8e, INSTR_VRR_VVVM0MV }, + { "vfpso", 0xcc, INSTR_VRR_VV00MMM }, + { "vfsq", 0xce, INSTR_VRR_VV000MM }, + { "vfs", 0xe2, INSTR_VRR_VVV00MM }, + { "vftci", 0x4a, INSTR_VRI_VVIMM }, +#endif +}; + static struct s390_insn opcode_eb[] = { #ifdef CONFIG_64BIT { "lmg", 0x04, INSTR_RSY_RRRD }, @@ -1552,16 +1772,17 @@ static struct s390_insn opcode_ed[] = { static unsigned int extract_operand(unsigned char *code, const struct s390_operand *operand) { + unsigned char *cp; unsigned int val; int bits; /* Extract fragments of the operand byte for byte. */ - code += operand->shift / 8; + cp = code + operand->shift / 8; bits = (operand->shift & 7) + operand->bits; val = 0; do { val <<= 8; - val |= (unsigned int) *code++; + val |= (unsigned int) *cp++; bits -= 8; } while (bits > 0); val >>= -bits; @@ -1571,6 +1792,18 @@ static unsigned int extract_operand(unsigned char *code, if (operand->bits == 20 && operand->shift == 20) val = (val & 0xff) << 12 | (val & 0xfff00) >> 8; + /* Check for register extensions bits for vector registers. */ + if (operand->flags & OPERAND_VR) { + if (operand->shift == 8) + val |= (code[4] & 8) << 1; + else if (operand->shift == 12) + val |= (code[4] & 4) << 2; + else if (operand->shift == 16) + val |= (code[4] & 2) << 3; + else if (operand->shift == 32) + val |= (code[4] & 1) << 4; + } + /* Sign extend value if the operand is signed or pc relative. */ if ((operand->flags & (OPERAND_SIGNED | OPERAND_PCREL)) && (val & (1U << (operand->bits - 1)))) @@ -1639,6 +1872,10 @@ struct s390_insn *find_insn(unsigned char *code) case 0xe5: table = opcode_e5; break; + case 0xe7: + table = opcode_e7; + opfrag = code[5]; + break; case 0xeb: table = opcode_eb; opfrag = code[5]; @@ -1734,6 +1971,8 @@ static int print_insn(char *buffer, unsigned char *code, unsigned long addr) ptr += sprintf(ptr, "%%a%i", value); else if (operand->flags & OPERAND_CR) ptr += sprintf(ptr, "%%c%i", value); + else if (operand->flags & OPERAND_VR) + ptr += sprintf(ptr, "%%v%i", value); else if (operand->flags & OPERAND_PCREL) ptr += sprintf(ptr, "%lx", (signed int) value + addr); diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 0dff972a169c16dd533c8256de9771e96f459e01..cef2879edff321e151972f059973cd7e9babca68 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -390,10 +390,10 @@ static __init void detect_machine_facilities(void) S390_lowcore.machine_flags |= MACHINE_FLAG_LPP; if (test_facility(50) && test_facility(73)) S390_lowcore.machine_flags |= MACHINE_FLAG_TE; - if (test_facility(66)) - S390_lowcore.machine_flags |= MACHINE_FLAG_RRBM; if (test_facility(51)) S390_lowcore.machine_flags |= MACHINE_FLAG_TLB_LC; + if (test_facility(129)) + S390_lowcore.machine_flags |= MACHINE_FLAG_VX; #endif } diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h index 1aad48398d06124541ef7936aacdb8d08f26c038..0554b9771c9f29eb727d40f0552258403e3201e3 100644 --- a/arch/s390/kernel/entry.h +++ b/arch/s390/kernel/entry.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include extern void *restart_stack; extern unsigned long suspend_zero_pages; @@ -21,6 +21,8 @@ void psw_idle(struct s390_idle_data *, unsigned long); asmlinkage long do_syscall_trace_enter(struct pt_regs *regs); asmlinkage void do_syscall_trace_exit(struct pt_regs *regs); +int alloc_vector_registers(struct task_struct *tsk); + void do_protection_exception(struct pt_regs *regs); void do_dat_exception(struct pt_regs *regs); @@ -43,8 +45,10 @@ void special_op_exception(struct pt_regs *regs); void specification_exception(struct pt_regs *regs); void transaction_exception(struct pt_regs *regs); void translation_exception(struct pt_regs *regs); +void vector_exception(struct pt_regs *regs); void do_per_trap(struct pt_regs *regs); +void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str); void syscall_trace(struct pt_regs *regs, int entryexit); void kernel_stack_overflow(struct pt_regs * regs); void do_signal(struct pt_regs *regs); diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index f2e674c702e1f904de800b94c33bedfd5fd30b91..7b2e03afd0177cda23a6d8424f0fe8934f705648 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S @@ -42,7 +42,8 @@ STACK_SHIFT = PAGE_SHIFT + THREAD_ORDER STACK_SIZE = 1 << STACK_SHIFT STACK_INIT = STACK_SIZE - STACK_FRAME_OVERHEAD - __PT_SIZE -_TIF_WORK = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED) +_TIF_WORK = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \ + _TIF_UPROBE) _TIF_TRACE = (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP | \ _TIF_SYSCALL_TRACEPOINT) _CIF_WORK = (_CIF_MCCK_PENDING | _CIF_ASCE) @@ -265,6 +266,10 @@ sysc_work: jo sysc_mcck_pending tm __TI_flags+7(%r12),_TIF_NEED_RESCHED jo sysc_reschedule +#ifdef CONFIG_UPROBES + tm __TI_flags+7(%r12),_TIF_UPROBE + jo sysc_uprobe_notify +#endif tm __PT_FLAGS+7(%r11),_PIF_PER_TRAP jo sysc_singlestep tm __TI_flags+7(%r12),_TIF_SIGPENDING @@ -322,6 +327,16 @@ sysc_notify_resume: larl %r14,sysc_return jg do_notify_resume +# +# _TIF_UPROBE is set, call uprobe_notify_resume +# +#ifdef CONFIG_UPROBES +sysc_uprobe_notify: + lgr %r2,%r11 # pass pointer to pt_regs + larl %r14,sysc_return + jg uprobe_notify_resume +#endif + # # _PIF_PER_TRAP is set, call do_per_trap # diff --git a/arch/s390/kernel/ftrace.c b/arch/s390/kernel/ftrace.c index 54d6493c4a561b050ad5e47bb21b6a041b6d496f..51d14fe5eb9a318349876eb376b359695770a0a2 100644 --- a/arch/s390/kernel/ftrace.c +++ b/arch/s390/kernel/ftrace.c @@ -1,7 +1,7 @@ /* * Dynamic function tracer architecture backend. * - * Copyright IBM Corp. 2009 + * Copyright IBM Corp. 2009,2014 * * Author(s): Heiko Carstens , * Martin Schwidefsky @@ -17,100 +17,76 @@ #include #include "entry.h" -#ifdef CONFIG_DYNAMIC_FTRACE - +void mcount_replace_code(void); void ftrace_disable_code(void); void ftrace_enable_insn(void); -#ifdef CONFIG_64BIT /* - * The 64-bit mcount code looks like this: + * The mcount code looks like this: * stg %r14,8(%r15) # offset 0 - * > larl %r1,<&counter> # offset 6 - * > brasl %r14,_mcount # offset 12 + * larl %r1,<&counter> # offset 6 + * brasl %r14,_mcount # offset 12 * lg %r14,8(%r15) # offset 18 - * Total length is 24 bytes. The middle two instructions of the mcount - * block get overwritten by ftrace_make_nop / ftrace_make_call. - * The 64-bit enabled ftrace code block looks like this: - * stg %r14,8(%r15) # offset 0 + * Total length is 24 bytes. The complete mcount block initially gets replaced + * by ftrace_make_nop. Subsequent calls to ftrace_make_call / ftrace_make_nop + * only patch the jg/lg instruction within the block. + * Note: we do not patch the first instruction to an unconditional branch, + * since that would break kprobes/jprobes. It is easier to leave the larl + * instruction in and only modify the second instruction. + * The enabled ftrace code block looks like this: + * larl %r0,.+24 # offset 0 * > lg %r1,__LC_FTRACE_FUNC # offset 6 - * > lgr %r0,%r0 # offset 12 - * > basr %r14,%r1 # offset 16 - * lg %r14,8(%15) # offset 18 - * The return points of the mcount/ftrace function have the same offset 18. - * The 64-bit disable ftrace code block looks like this: - * stg %r14,8(%r15) # offset 0 + * br %r1 # offset 12 + * brcl 0,0 # offset 14 + * brc 0,0 # offset 20 + * The ftrace function gets called with a non-standard C function call ABI + * where r0 contains the return address. It is also expected that the called + * function only clobbers r0 and r1, but restores r2-r15. + * The return point of the ftrace function has offset 24, so execution + * continues behind the mcount block. + * larl %r0,.+24 # offset 0 * > jg .+18 # offset 6 - * > lgr %r0,%r0 # offset 12 - * > basr %r14,%r1 # offset 16 - * lg %r14,8(%15) # offset 18 + * br %r1 # offset 12 + * brcl 0,0 # offset 14 + * brc 0,0 # offset 20 * The jg instruction branches to offset 24 to skip as many instructions * as possible. */ asm( " .align 4\n" + "mcount_replace_code:\n" + " larl %r0,0f\n" "ftrace_disable_code:\n" " jg 0f\n" - " lgr %r0,%r0\n" - " basr %r14,%r1\n" + " br %r1\n" + " brcl 0,0\n" + " brc 0,0\n" "0:\n" " .align 4\n" "ftrace_enable_insn:\n" " lg %r1,"__stringify(__LC_FTRACE_FUNC)"\n"); +#define MCOUNT_BLOCK_SIZE 24 +#define MCOUNT_INSN_OFFSET 6 #define FTRACE_INSN_SIZE 6 -#else /* CONFIG_64BIT */ -/* - * The 31-bit mcount code looks like this: - * st %r14,4(%r15) # offset 0 - * > bras %r1,0f # offset 4 - * > .long _mcount # offset 8 - * > .long <&counter> # offset 12 - * > 0: l %r14,0(%r1) # offset 16 - * > l %r1,4(%r1) # offset 20 - * basr %r14,%r14 # offset 24 - * l %r14,4(%r15) # offset 26 - * Total length is 30 bytes. The twenty bytes starting from offset 4 - * to offset 24 get overwritten by ftrace_make_nop / ftrace_make_call. - * The 31-bit enabled ftrace code block looks like this: - * st %r14,4(%r15) # offset 0 - * > l %r14,__LC_FTRACE_FUNC # offset 4 - * > j 0f # offset 8 - * > .fill 12,1,0x07 # offset 12 - * 0: basr %r14,%r14 # offset 24 - * l %r14,4(%r14) # offset 26 - * The return points of the mcount/ftrace function have the same offset 26. - * The 31-bit disabled ftrace code block looks like this: - * st %r14,4(%r15) # offset 0 - * > j .+26 # offset 4 - * > j 0f # offset 8 - * > .fill 12,1,0x07 # offset 12 - * 0: basr %r14,%r14 # offset 24 - * l %r14,4(%r14) # offset 26 - * The j instruction branches to offset 30 to skip as many instructions - * as possible. - */ -asm( - " .align 4\n" - "ftrace_disable_code:\n" - " j 1f\n" - " j 0f\n" - " .fill 12,1,0x07\n" - "0: basr %r14,%r14\n" - "1:\n" - " .align 4\n" - "ftrace_enable_insn:\n" - " l %r14,"__stringify(__LC_FTRACE_FUNC)"\n"); - -#define FTRACE_INSN_SIZE 4 - -#endif /* CONFIG_64BIT */ - +int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, + unsigned long addr) +{ + return 0; +} int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long addr) { + /* Initial replacement of the whole mcount block */ + if (addr == MCOUNT_ADDR) { + if (probe_kernel_write((void *) rec->ip - MCOUNT_INSN_OFFSET, + mcount_replace_code, + MCOUNT_BLOCK_SIZE)) + return -EPERM; + return 0; + } if (probe_kernel_write((void *) rec->ip, ftrace_disable_code, MCOUNT_INSN_SIZE)) return -EPERM; @@ -135,8 +111,6 @@ int __init ftrace_dyn_arch_init(void) return 0; } -#endif /* CONFIG_DYNAMIC_FTRACE */ - #ifdef CONFIG_FUNCTION_GRAPH_TRACER /* * Hook the return address and push it in the stack of return addresses @@ -162,31 +136,26 @@ unsigned long __kprobes prepare_ftrace_return(unsigned long parent, return parent; } -#ifdef CONFIG_DYNAMIC_FTRACE /* * Patch the kernel code at ftrace_graph_caller location. The instruction - * there is branch relative and save to prepare_ftrace_return. To disable - * the call to prepare_ftrace_return we patch the bras offset to point - * directly after the instructions. To enable the call we calculate - * the original offset to prepare_ftrace_return and put it back. + * there is branch relative on condition. To enable the ftrace graph code + * block, we simply patch the mask field of the instruction to zero and + * turn the instruction into a nop. + * To disable the ftrace graph code the mask field will be patched to + * all ones, which turns the instruction into an unconditional branch. */ int ftrace_enable_ftrace_graph_caller(void) { - unsigned short offset; + u8 op = 0x04; /* set mask field to zero */ - offset = ((void *) prepare_ftrace_return - - (void *) ftrace_graph_caller) / 2; - return probe_kernel_write((void *) ftrace_graph_caller + 2, - &offset, sizeof(offset)); + return probe_kernel_write(__va(ftrace_graph_caller)+1, &op, sizeof(op)); } int ftrace_disable_ftrace_graph_caller(void) { - static unsigned short offset = 0x0002; + u8 op = 0xf4; /* set mask field to all ones */ - return probe_kernel_write((void *) ftrace_graph_caller + 2, - &offset, sizeof(offset)); + return probe_kernel_write(__va(ftrace_graph_caller)+1, &op, sizeof(op)); } -#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ diff --git a/arch/s390/kernel/head.S b/arch/s390/kernel/head.S index e88d35d749501e43b296e8794351f691368dcf5e..d62eee11f0b54240339d898fbf95d4299c011545 100644 --- a/arch/s390/kernel/head.S +++ b/arch/s390/kernel/head.S @@ -398,7 +398,7 @@ ENTRY(startup_kdump) xc __LC_STFL_FAC_LIST(8),__LC_STFL_FAC_LIST #ifndef CONFIG_MARCH_G5 # check capabilities against MARCH_{G5,Z900,Z990,Z9_109,Z10} - .insn s,0xb2b10000,__LC_STFL_FAC_LIST # store facility list + .insn s,0xb2b10000,0 # store facilities @ __LC_STFL_FAC_LIST tm __LC_STFL_FAC_LIST,0x01 # stfle available ? jz 0f la %r0,1 diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c new file mode 100644 index 0000000000000000000000000000000000000000..7559f1beab2918f84831791ae93a901cd826bb57 --- /dev/null +++ b/arch/s390/kernel/idle.c @@ -0,0 +1,124 @@ +/* + * Idle functions for s390. + * + * Copyright IBM Corp. 2014 + * + * Author(s): Martin Schwidefsky + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "entry.h" + +static DEFINE_PER_CPU(struct s390_idle_data, s390_idle); + +void __kprobes enabled_wait(void) +{ + struct s390_idle_data *idle = this_cpu_ptr(&s390_idle); + unsigned long long idle_time; + unsigned long psw_mask; + + trace_hardirqs_on(); + + /* Wait for external, I/O or machine check interrupt. */ + psw_mask = PSW_KERNEL_BITS | PSW_MASK_WAIT | PSW_MASK_DAT | + PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK; + clear_cpu_flag(CIF_NOHZ_DELAY); + + /* Call the assembler magic in entry.S */ + psw_idle(idle, psw_mask); + + /* Account time spent with enabled wait psw loaded as idle time. */ + idle->sequence++; + smp_wmb(); + idle_time = idle->clock_idle_exit - idle->clock_idle_enter; + idle->clock_idle_enter = idle->clock_idle_exit = 0ULL; + idle->idle_time += idle_time; + idle->idle_count++; + account_idle_time(idle_time); + smp_wmb(); + idle->sequence++; +} + +static ssize_t show_idle_count(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); + unsigned long long idle_count; + unsigned int sequence; + + do { + sequence = ACCESS_ONCE(idle->sequence); + idle_count = ACCESS_ONCE(idle->idle_count); + if (ACCESS_ONCE(idle->clock_idle_enter)) + idle_count++; + } while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence)); + return sprintf(buf, "%llu\n", idle_count); +} +DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL); + +static ssize_t show_idle_time(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); + unsigned long long now, idle_time, idle_enter, idle_exit; + unsigned int sequence; + + do { + now = get_tod_clock(); + sequence = ACCESS_ONCE(idle->sequence); + idle_time = ACCESS_ONCE(idle->idle_time); + idle_enter = ACCESS_ONCE(idle->clock_idle_enter); + idle_exit = ACCESS_ONCE(idle->clock_idle_exit); + } while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence)); + idle_time += idle_enter ? ((idle_exit ? : now) - idle_enter) : 0; + return sprintf(buf, "%llu\n", idle_time >> 12); +} +DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL); + +cputime64_t arch_cpu_idle_time(int cpu) +{ + struct s390_idle_data *idle = &per_cpu(s390_idle, cpu); + unsigned long long now, idle_enter, idle_exit; + unsigned int sequence; + + do { + now = get_tod_clock(); + sequence = ACCESS_ONCE(idle->sequence); + idle_enter = ACCESS_ONCE(idle->clock_idle_enter); + idle_exit = ACCESS_ONCE(idle->clock_idle_exit); + } while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence)); + return idle_enter ? ((idle_exit ?: now) - idle_enter) : 0; +} + +void arch_cpu_idle_enter(void) +{ + local_mcck_disable(); +} + +void arch_cpu_idle(void) +{ + if (!test_cpu_flag(CIF_MCCK_PENDING)) + /* Halt the cpu and keep track of cpu time accounting. */ + enabled_wait(); + local_irq_enable(); +} + +void arch_cpu_idle_exit(void) +{ + local_mcck_enable(); + if (test_cpu_flag(CIF_MCCK_PENDING)) + s390_handle_mcck(); +} + +void arch_cpu_idle_dead(void) +{ + cpu_die(); +} diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 8eb82443cfbdc812ebb56d67112e6c4b50ef2a2b..1b8a38ab7861c685af7d9b1b7f593a9733135909 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -70,6 +70,7 @@ static const struct irq_class irqclass_sub_desc[NR_ARCH_IRQS] = { {.irq = IRQEXT_CMS, .name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling"}, {.irq = IRQEXT_CMC, .name = "CMC", .desc = "[EXT] CPU-Measurement: Counter"}, {.irq = IRQEXT_CMR, .name = "CMR", .desc = "[EXT] CPU-Measurement: RI"}, + {.irq = IRQEXT_FTP, .name = "FTP", .desc = "[EXT] HMC FTP Service"}, {.irq = IRQIO_CIO, .name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt"}, {.irq = IRQIO_QAI, .name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt"}, {.irq = IRQIO_DAS, .name = "DAS", .desc = "[I/O] DASD"}, @@ -258,7 +259,7 @@ static irqreturn_t do_ext_interrupt(int irq, void *dummy) ext_code = *(struct ext_code *) ®s->int_code; if (ext_code.code != EXT_IRQ_CLK_COMP) - __get_cpu_var(s390_idle).nohz_delay = 1; + set_cpu_flag(CIF_NOHZ_DELAY); index = ext_hash(ext_code.code); rcu_read_lock(); diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index bc71a7b95af546b94d6ac6ba74c6484bbb261069..014d4729b134d0abb526044f64d4e81a7a262261 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c @@ -58,161 +58,13 @@ struct kprobe_insn_cache kprobe_dmainsn_slots = { .insn_size = MAX_INSN_SIZE, }; -static int __kprobes is_prohibited_opcode(kprobe_opcode_t *insn) -{ - if (!is_known_insn((unsigned char *)insn)) - return -EINVAL; - switch (insn[0] >> 8) { - case 0x0c: /* bassm */ - case 0x0b: /* bsm */ - case 0x83: /* diag */ - case 0x44: /* ex */ - case 0xac: /* stnsm */ - case 0xad: /* stosm */ - return -EINVAL; - case 0xc6: - switch (insn[0] & 0x0f) { - case 0x00: /* exrl */ - return -EINVAL; - } - } - switch (insn[0]) { - case 0x0101: /* pr */ - case 0xb25a: /* bsa */ - case 0xb240: /* bakr */ - case 0xb258: /* bsg */ - case 0xb218: /* pc */ - case 0xb228: /* pt */ - case 0xb98d: /* epsw */ - return -EINVAL; - } - return 0; -} - -static int __kprobes get_fixup_type(kprobe_opcode_t *insn) -{ - /* default fixup method */ - int fixup = FIXUP_PSW_NORMAL; - - switch (insn[0] >> 8) { - case 0x05: /* balr */ - case 0x0d: /* basr */ - fixup = FIXUP_RETURN_REGISTER; - /* if r2 = 0, no branch will be taken */ - if ((insn[0] & 0x0f) == 0) - fixup |= FIXUP_BRANCH_NOT_TAKEN; - break; - case 0x06: /* bctr */ - case 0x07: /* bcr */ - fixup = FIXUP_BRANCH_NOT_TAKEN; - break; - case 0x45: /* bal */ - case 0x4d: /* bas */ - fixup = FIXUP_RETURN_REGISTER; - break; - case 0x47: /* bc */ - case 0x46: /* bct */ - case 0x86: /* bxh */ - case 0x87: /* bxle */ - fixup = FIXUP_BRANCH_NOT_TAKEN; - break; - case 0x82: /* lpsw */ - fixup = FIXUP_NOT_REQUIRED; - break; - case 0xb2: /* lpswe */ - if ((insn[0] & 0xff) == 0xb2) - fixup = FIXUP_NOT_REQUIRED; - break; - case 0xa7: /* bras */ - if ((insn[0] & 0x0f) == 0x05) - fixup |= FIXUP_RETURN_REGISTER; - break; - case 0xc0: - if ((insn[0] & 0x0f) == 0x05) /* brasl */ - fixup |= FIXUP_RETURN_REGISTER; - break; - case 0xeb: - switch (insn[2] & 0xff) { - case 0x44: /* bxhg */ - case 0x45: /* bxleg */ - fixup = FIXUP_BRANCH_NOT_TAKEN; - break; - } - break; - case 0xe3: /* bctg */ - if ((insn[2] & 0xff) == 0x46) - fixup = FIXUP_BRANCH_NOT_TAKEN; - break; - case 0xec: - switch (insn[2] & 0xff) { - case 0xe5: /* clgrb */ - case 0xe6: /* cgrb */ - case 0xf6: /* crb */ - case 0xf7: /* clrb */ - case 0xfc: /* cgib */ - case 0xfd: /* cglib */ - case 0xfe: /* cib */ - case 0xff: /* clib */ - fixup = FIXUP_BRANCH_NOT_TAKEN; - break; - } - break; - } - return fixup; -} - -static int __kprobes is_insn_relative_long(kprobe_opcode_t *insn) -{ - /* Check if we have a RIL-b or RIL-c format instruction which - * we need to modify in order to avoid instruction emulation. */ - switch (insn[0] >> 8) { - case 0xc0: - if ((insn[0] & 0x0f) == 0x00) /* larl */ - return true; - break; - case 0xc4: - switch (insn[0] & 0x0f) { - case 0x02: /* llhrl */ - case 0x04: /* lghrl */ - case 0x05: /* lhrl */ - case 0x06: /* llghrl */ - case 0x07: /* sthrl */ - case 0x08: /* lgrl */ - case 0x0b: /* stgrl */ - case 0x0c: /* lgfrl */ - case 0x0d: /* lrl */ - case 0x0e: /* llgfrl */ - case 0x0f: /* strl */ - return true; - } - break; - case 0xc6: - switch (insn[0] & 0x0f) { - case 0x02: /* pfdrl */ - case 0x04: /* cghrl */ - case 0x05: /* chrl */ - case 0x06: /* clghrl */ - case 0x07: /* clhrl */ - case 0x08: /* cgrl */ - case 0x0a: /* clgrl */ - case 0x0c: /* cgfrl */ - case 0x0d: /* crl */ - case 0x0e: /* clgfrl */ - case 0x0f: /* clrl */ - return true; - } - break; - } - return false; -} - static void __kprobes copy_instruction(struct kprobe *p) { s64 disp, new_disp; u64 addr, new_addr; memcpy(p->ainsn.insn, p->addr, insn_length(p->opcode >> 8)); - if (!is_insn_relative_long(p->ainsn.insn)) + if (!probe_is_insn_relative_long(p->ainsn.insn)) return; /* * For pc-relative instructions in RIL-b or RIL-c format patch the @@ -276,7 +128,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) if ((unsigned long) p->addr & 0x01) return -EINVAL; /* Make sure the probe isn't going on a difficult instruction */ - if (is_prohibited_opcode(p->addr)) + if (probe_is_prohibited_opcode(p->addr)) return -EINVAL; if (s390_get_insn_slot(p)) return -ENOMEM; @@ -366,9 +218,9 @@ static void __kprobes disable_singlestep(struct kprobe_ctlblk *kcb, */ static void __kprobes push_kprobe(struct kprobe_ctlblk *kcb, struct kprobe *p) { - kcb->prev_kprobe.kp = __get_cpu_var(current_kprobe); + kcb->prev_kprobe.kp = __this_cpu_read(current_kprobe); kcb->prev_kprobe.status = kcb->kprobe_status; - __get_cpu_var(current_kprobe) = p; + __this_cpu_write(current_kprobe, p); } /* @@ -378,7 +230,7 @@ static void __kprobes push_kprobe(struct kprobe_ctlblk *kcb, struct kprobe *p) */ static void __kprobes pop_kprobe(struct kprobe_ctlblk *kcb) { - __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp; + __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp); kcb->kprobe_status = kcb->prev_kprobe.status; } @@ -459,7 +311,7 @@ static int __kprobes kprobe_handler(struct pt_regs *regs) enable_singlestep(kcb, regs, (unsigned long) p->ainsn.insn); return 1; } else if (kprobe_running()) { - p = __get_cpu_var(current_kprobe); + p = __this_cpu_read(current_kprobe); if (p->break_handler && p->break_handler(p, regs)) { /* * Continuation after the jprobe completed and @@ -605,7 +457,7 @@ static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs) { struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); unsigned long ip = regs->psw.addr & PSW_ADDR_INSN; - int fixup = get_fixup_type(p->ainsn.insn); + int fixup = probe_get_fixup_type(p->ainsn.insn); if (fixup & FIXUP_PSW_NORMAL) ip += (unsigned long) p->addr - (unsigned long) p->ainsn.insn; @@ -789,11 +641,6 @@ void __kprobes jprobe_return(void) asm volatile(".word 0x0002"); } -static void __used __kprobes jprobe_return_end(void) -{ - asm volatile("bcr 0,0"); -} - int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) { struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c index 719e27b2cf2264b4000d65635ad26b6e06284043..4685337fa7c6bf9464630f5ec648ca65e53cea0c 100644 --- a/arch/s390/kernel/machine_kexec.c +++ b/arch/s390/kernel/machine_kexec.c @@ -25,6 +25,7 @@ #include #include #include +#include typedef void (*relocate_kernel_t)(kimage_entry_t *, unsigned long); @@ -43,7 +44,7 @@ static void add_elf_notes(int cpu) memcpy((void *) (4608UL + sa->pref_reg), sa, sizeof(*sa)); ptr = (u64 *) per_cpu_ptr(crash_notes, cpu); - ptr = fill_cpu_elf_notes(ptr, sa); + ptr = fill_cpu_elf_notes(ptr, sa, NULL); memset(ptr, 0, sizeof(struct elf_note)); } @@ -53,8 +54,11 @@ static void add_elf_notes(int cpu) static void setup_regs(void) { unsigned long sa = S390_lowcore.prefixreg_save_area + SAVE_AREA_BASE; + struct _lowcore *lc; int cpu, this_cpu; + /* Get lowcore pointer from store status of this CPU (absolute zero) */ + lc = (struct _lowcore *)(unsigned long)S390_lowcore.prefixreg_save_area; this_cpu = smp_find_processor_id(stap()); add_elf_notes(this_cpu); for_each_online_cpu(cpu) { @@ -64,6 +68,8 @@ static void setup_regs(void) continue; add_elf_notes(cpu); } + if (MACHINE_HAS_VX) + save_vx_regs_safe((void *) lc->vector_save_area_addr); /* Copy dump CPU store status info to absolute zero */ memcpy((void *) SAVE_AREA_BASE, (void *) sa, sizeof(struct save_area)); } diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S index 433c6dbfa442439b1b7cd5ce8c8b5a9e5566b44d..4300ea3748263d582d3baee7f1cdf8a7e2499dd6 100644 --- a/arch/s390/kernel/mcount.S +++ b/arch/s390/kernel/mcount.S @@ -8,62 +8,72 @@ #include #include #include +#include .section .kprobes.text, "ax" ENTRY(ftrace_stub) br %r14 +#define STACK_FRAME_SIZE (STACK_FRAME_OVERHEAD + __PT_SIZE) +#define STACK_PTREGS (STACK_FRAME_OVERHEAD) +#define STACK_PTREGS_GPRS (STACK_PTREGS + __PT_GPRS) +#define STACK_PTREGS_PSW (STACK_PTREGS + __PT_PSW) + ENTRY(_mcount) -#ifdef CONFIG_DYNAMIC_FTRACE br %r14 ENTRY(ftrace_caller) + .globl ftrace_regs_caller + .set ftrace_regs_caller,ftrace_caller + lgr %r1,%r15 + aghi %r15,-STACK_FRAME_SIZE + stg %r1,__SF_BACKCHAIN(%r15) + stg %r1,(STACK_PTREGS_GPRS+15*8)(%r15) + stg %r0,(STACK_PTREGS_PSW+8)(%r15) + stmg %r2,%r14,(STACK_PTREGS_GPRS+2*8)(%r15) +#ifdef CONFIG_HAVE_MARCH_Z196_FEATURES + aghik %r2,%r0,-MCOUNT_INSN_SIZE + lgrl %r4,function_trace_op + lgrl %r1,ftrace_trace_function +#else + lgr %r2,%r0 + aghi %r2,-MCOUNT_INSN_SIZE + larl %r4,function_trace_op + lg %r4,0(%r4) + larl %r1,ftrace_trace_function + lg %r1,0(%r1) #endif - stm %r2,%r5,16(%r15) - bras %r1,1f -0: .long ftrace_trace_function -1: st %r14,56(%r15) - lr %r0,%r15 - ahi %r15,-96 - l %r3,100(%r15) - la %r2,0(%r14) - st %r0,__SF_BACKCHAIN(%r15) - la %r3,0(%r3) - ahi %r2,-MCOUNT_INSN_SIZE - l %r14,0b-0b(%r1) - l %r14,0(%r14) - basr %r14,%r14 + lgr %r3,%r14 + la %r5,STACK_PTREGS(%r15) + basr %r14,%r1 #ifdef CONFIG_FUNCTION_GRAPH_TRACER - l %r2,100(%r15) - l %r3,152(%r15) +# The j instruction gets runtime patched to a nop instruction. +# See ftrace_enable_ftrace_graph_caller. ENTRY(ftrace_graph_caller) -# The bras instruction gets runtime patched to call prepare_ftrace_return. -# See ftrace_enable_ftrace_graph_caller. The patched instruction is: -# bras %r14,prepare_ftrace_return - bras %r14,0f -0: st %r2,100(%r15) + j ftrace_graph_caller_end + lg %r2,(STACK_PTREGS_GPRS+14*8)(%r15) + lg %r3,(STACK_PTREGS_PSW+8)(%r15) + brasl %r14,prepare_ftrace_return + stg %r2,(STACK_PTREGS_GPRS+14*8)(%r15) +ftrace_graph_caller_end: + .globl ftrace_graph_caller_end #endif - ahi %r15,96 - l %r14,56(%r15) - lm %r2,%r5,16(%r15) - br %r14 + lg %r1,(STACK_PTREGS_PSW+8)(%r15) + lmg %r2,%r15,(STACK_PTREGS_GPRS+2*8)(%r15) + br %r1 #ifdef CONFIG_FUNCTION_GRAPH_TRACER ENTRY(return_to_handler) - stm %r2,%r5,16(%r15) - st %r14,56(%r15) - lr %r0,%r15 - ahi %r15,-96 - st %r0,__SF_BACKCHAIN(%r15) - bras %r1,0f - .long ftrace_return_to_handler -0: l %r2,0b-0b(%r1) - basr %r14,%r2 - lr %r14,%r2 - ahi %r15,96 - lm %r2,%r5,16(%r15) + stmg %r2,%r5,32(%r15) + lgr %r1,%r15 + aghi %r15,-STACK_FRAME_OVERHEAD + stg %r1,__SF_BACKCHAIN(%r15) + brasl %r14,ftrace_return_to_handler + aghi %r15,STACK_FRAME_OVERHEAD + lgr %r14,%r2 + lmg %r2,%r5,32(%r15) br %r14 #endif diff --git a/arch/s390/kernel/mcount64.S b/arch/s390/kernel/mcount64.S deleted file mode 100644 index c67a8bf0fd9afa48f4d906cffc7cc5e2594ebf07..0000000000000000000000000000000000000000 --- a/arch/s390/kernel/mcount64.S +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright IBM Corp. 2008, 2009 - * - * Author(s): Heiko Carstens , - * - */ - -#include -#include -#include - - .section .kprobes.text, "ax" - -ENTRY(ftrace_stub) - br %r14 - -ENTRY(_mcount) -#ifdef CONFIG_DYNAMIC_FTRACE - br %r14 - -ENTRY(ftrace_caller) -#endif - stmg %r2,%r5,32(%r15) - stg %r14,112(%r15) - lgr %r1,%r15 - aghi %r15,-160 - stg %r1,__SF_BACKCHAIN(%r15) - lgr %r2,%r14 - lg %r3,168(%r15) - aghi %r2,-MCOUNT_INSN_SIZE - larl %r14,ftrace_trace_function - lg %r14,0(%r14) - basr %r14,%r14 -#ifdef CONFIG_FUNCTION_GRAPH_TRACER - lg %r2,168(%r15) - lg %r3,272(%r15) -ENTRY(ftrace_graph_caller) -# The bras instruction gets runtime patched to call prepare_ftrace_return. -# See ftrace_enable_ftrace_graph_caller. The patched instruction is: -# bras %r14,prepare_ftrace_return - bras %r14,0f -0: stg %r2,168(%r15) -#endif - aghi %r15,160 - lmg %r2,%r5,32(%r15) - lg %r14,112(%r15) - br %r14 - -#ifdef CONFIG_FUNCTION_GRAPH_TRACER - -ENTRY(return_to_handler) - stmg %r2,%r5,32(%r15) - lgr %r1,%r15 - aghi %r15,-160 - stg %r1,__SF_BACKCHAIN(%r15) - brasl %r14,ftrace_return_to_handler - aghi %r15,160 - lgr %r14,%r2 - lmg %r2,%r5,32(%r15) - br %r14 - -#endif diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c index 210e1285f75a44e4fb931e527bdf786eeba6c5ee..dd1c24ceda50245978c97e13c5b992db3139f329 100644 --- a/arch/s390/kernel/nmi.c +++ b/arch/s390/kernel/nmi.c @@ -20,6 +20,7 @@ #include #include #include +#include struct mcck_struct { int kill_task; @@ -53,8 +54,12 @@ void s390_handle_mcck(void) */ local_irq_save(flags); local_mcck_disable(); - mcck = __get_cpu_var(cpu_mcck); - memset(&__get_cpu_var(cpu_mcck), 0, sizeof(struct mcck_struct)); + /* + * Ummm... Does this make sense at all? Copying the percpu struct + * and then zapping it one statement later? + */ + memcpy(&mcck, this_cpu_ptr(&cpu_mcck), sizeof(mcck)); + memset(&mcck, 0, sizeof(struct mcck_struct)); clear_cpu_flag(CIF_MCCK_PENDING); local_mcck_enable(); local_irq_restore(flags); @@ -163,6 +168,21 @@ static int notrace s390_revalidate_registers(struct mci *mci) " ld 15,120(%0)\n" : : "a" (fpt_save_area)); } + +#ifdef CONFIG_64BIT + /* Revalidate vector registers */ + if (MACHINE_HAS_VX && current->thread.vxrs) { + if (!mci->vr) { + /* + * Vector registers can't be restored and therefore + * the process needs to be terminated. + */ + kill_task = 1; + } + restore_vx_regs((__vector128 *) + S390_lowcore.vector_save_area_addr); + } +#endif /* Revalidate access registers */ asm volatile( " lam 0,15,0(%0)" @@ -253,7 +273,7 @@ void notrace s390_do_machine_check(struct pt_regs *regs) nmi_enter(); inc_irq_stat(NMI_NMI); mci = (struct mci *) &S390_lowcore.mcck_interruption_code; - mcck = &__get_cpu_var(cpu_mcck); + mcck = this_cpu_ptr(&cpu_mcck); umode = user_mode(regs); if (mci->sd) { diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c index d3194de7ae1e6bb42fdf7132f3109c08fc5e591f..56fdad479115eafe3ec4acd6f4e5f128d71737a7 100644 --- a/arch/s390/kernel/perf_cpum_cf.c +++ b/arch/s390/kernel/perf_cpum_cf.c @@ -173,7 +173,7 @@ static int validate_ctr_auth(const struct hw_perf_event *hwc) */ static void cpumf_pmu_enable(struct pmu *pmu) { - struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events); int err; if (cpuhw->flags & PMU_F_ENABLED) @@ -196,7 +196,7 @@ static void cpumf_pmu_enable(struct pmu *pmu) */ static void cpumf_pmu_disable(struct pmu *pmu) { - struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events); int err; u64 inactive; @@ -230,7 +230,7 @@ static void cpumf_measurement_alert(struct ext_code ext_code, return; inc_irq_stat(IRQEXT_CMC); - cpuhw = &__get_cpu_var(cpu_hw_events); + cpuhw = this_cpu_ptr(&cpu_hw_events); /* Measurement alerts are shared and might happen when the PMU * is not reserved. Ignore these alerts in this case. */ @@ -250,7 +250,7 @@ static void cpumf_measurement_alert(struct ext_code ext_code, #define PMC_RELEASE 1 static void setup_pmc_cpu(void *flags) { - struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events); switch (*((int *) flags)) { case PMC_INIT: @@ -475,7 +475,7 @@ static void cpumf_pmu_read(struct perf_event *event) static void cpumf_pmu_start(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc = &event->hw; if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED))) @@ -506,7 +506,7 @@ static void cpumf_pmu_start(struct perf_event *event, int flags) static void cpumf_pmu_stop(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc = &event->hw; if (!(hwc->state & PERF_HES_STOPPED)) { @@ -527,7 +527,7 @@ static void cpumf_pmu_stop(struct perf_event *event, int flags) static int cpumf_pmu_add(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events); /* Check authorization for the counter set to which this * counter belongs. @@ -551,7 +551,7 @@ static int cpumf_pmu_add(struct perf_event *event, int flags) static void cpumf_pmu_del(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events); cpumf_pmu_stop(event, PERF_EF_UPDATE); @@ -575,7 +575,7 @@ static void cpumf_pmu_del(struct perf_event *event, int flags) */ static void cpumf_pmu_start_txn(struct pmu *pmu) { - struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events); perf_pmu_disable(pmu); cpuhw->flags |= PERF_EVENT_TXN; @@ -589,7 +589,7 @@ static void cpumf_pmu_start_txn(struct pmu *pmu) */ static void cpumf_pmu_cancel_txn(struct pmu *pmu) { - struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events); WARN_ON(cpuhw->tx_state != cpuhw->state); @@ -604,7 +604,7 @@ static void cpumf_pmu_cancel_txn(struct pmu *pmu) */ static int cpumf_pmu_commit_txn(struct pmu *pmu) { - struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events); u64 state; /* check if the updated state can be scheduled */ diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index ea0c7b2ef030f2d855df012af852cab54e509287..08e761318c17d786d2e40ce85fcd0b631fecc731 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c @@ -562,7 +562,7 @@ static DEFINE_MUTEX(pmc_reserve_mutex); static void setup_pmc_cpu(void *flags) { int err; - struct cpu_hw_sf *cpusf = &__get_cpu_var(cpu_hw_sf); + struct cpu_hw_sf *cpusf = this_cpu_ptr(&cpu_hw_sf); err = 0; switch (*((int *) flags)) { @@ -849,7 +849,7 @@ static int cpumsf_pmu_event_init(struct perf_event *event) static void cpumsf_pmu_enable(struct pmu *pmu) { - struct cpu_hw_sf *cpuhw = &__get_cpu_var(cpu_hw_sf); + struct cpu_hw_sf *cpuhw = this_cpu_ptr(&cpu_hw_sf); struct hw_perf_event *hwc; int err; @@ -898,7 +898,7 @@ static void cpumsf_pmu_enable(struct pmu *pmu) static void cpumsf_pmu_disable(struct pmu *pmu) { - struct cpu_hw_sf *cpuhw = &__get_cpu_var(cpu_hw_sf); + struct cpu_hw_sf *cpuhw = this_cpu_ptr(&cpu_hw_sf); struct hws_lsctl_request_block inactive; struct hws_qsi_info_block si; int err; @@ -1306,7 +1306,7 @@ static void cpumsf_pmu_read(struct perf_event *event) */ static void cpumsf_pmu_start(struct perf_event *event, int flags) { - struct cpu_hw_sf *cpuhw = &__get_cpu_var(cpu_hw_sf); + struct cpu_hw_sf *cpuhw = this_cpu_ptr(&cpu_hw_sf); if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED))) return; @@ -1327,7 +1327,7 @@ static void cpumsf_pmu_start(struct perf_event *event, int flags) */ static void cpumsf_pmu_stop(struct perf_event *event, int flags) { - struct cpu_hw_sf *cpuhw = &__get_cpu_var(cpu_hw_sf); + struct cpu_hw_sf *cpuhw = this_cpu_ptr(&cpu_hw_sf); if (event->hw.state & PERF_HES_STOPPED) return; @@ -1346,7 +1346,7 @@ static void cpumsf_pmu_stop(struct perf_event *event, int flags) static int cpumsf_pmu_add(struct perf_event *event, int flags) { - struct cpu_hw_sf *cpuhw = &__get_cpu_var(cpu_hw_sf); + struct cpu_hw_sf *cpuhw = this_cpu_ptr(&cpu_hw_sf); int err; if (cpuhw->flags & PMU_F_IN_USE) @@ -1397,7 +1397,7 @@ static int cpumsf_pmu_add(struct perf_event *event, int flags) static void cpumsf_pmu_del(struct perf_event *event, int flags) { - struct cpu_hw_sf *cpuhw = &__get_cpu_var(cpu_hw_sf); + struct cpu_hw_sf *cpuhw = this_cpu_ptr(&cpu_hw_sf); perf_pmu_disable(event->pmu); cpumsf_pmu_stop(event, PERF_EF_UPDATE); @@ -1470,7 +1470,7 @@ static void cpumf_measurement_alert(struct ext_code ext_code, if (!(alert & CPU_MF_INT_SF_MASK)) return; inc_irq_stat(IRQEXT_CMS); - cpuhw = &__get_cpu_var(cpu_hw_sf); + cpuhw = this_cpu_ptr(&cpu_hw_sf); /* Measurement alerts are shared and might happen when the PMU * is not reserved. Ignore these alerts in this case. */ diff --git a/arch/s390/kernel/pgm_check.S b/arch/s390/kernel/pgm_check.S index 813ec7260878662d097ba90ef04d90b6f183367d..f6f8886399f64e1ebcf7a6a2662451286ecc1ec1 100644 --- a/arch/s390/kernel/pgm_check.S +++ b/arch/s390/kernel/pgm_check.S @@ -49,7 +49,7 @@ PGM_CHECK_DEFAULT /* 17 */ PGM_CHECK_64BIT(transaction_exception) /* 18 */ PGM_CHECK_DEFAULT /* 19 */ PGM_CHECK_DEFAULT /* 1a */ -PGM_CHECK_DEFAULT /* 1b */ +PGM_CHECK_64BIT(vector_exception) /* 1b */ PGM_CHECK(space_switch_exception) /* 1c */ PGM_CHECK(hfp_sqrt_exception) /* 1d */ PGM_CHECK_DEFAULT /* 1e */ diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 93b9ca42e5c0ace2ac209789befa3242850b0043..ed84cc224899ff76ca30e5755d7cf7f2a734d981 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -61,30 +61,6 @@ unsigned long thread_saved_pc(struct task_struct *tsk) return sf->gprs[8]; } -void arch_cpu_idle(void) -{ - local_mcck_disable(); - if (test_cpu_flag(CIF_MCCK_PENDING)) { - local_mcck_enable(); - local_irq_enable(); - return; - } - /* Halt the cpu and keep track of cpu time accounting. */ - vtime_stop_cpu(); - local_irq_enable(); -} - -void arch_cpu_idle_exit(void) -{ - if (test_cpu_flag(CIF_MCCK_PENDING)) - s390_handle_mcck(); -} - -void arch_cpu_idle_dead(void) -{ - cpu_die(); -} - extern void __kprobes kernel_thread_starter(void); /* diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c index 24612029f4502064b64c53ba52426c52a9337adc..dbdd33ee010204f0e03c9c1fd48ad30acad59b4c 100644 --- a/arch/s390/kernel/processor.c +++ b/arch/s390/kernel/processor.c @@ -23,15 +23,13 @@ static DEFINE_PER_CPU(struct cpuid, cpu_id); */ void cpu_init(void) { - struct s390_idle_data *idle = &__get_cpu_var(s390_idle); - struct cpuid *id = &__get_cpu_var(cpu_id); + struct cpuid *id = this_cpu_ptr(&cpu_id); get_cpu_id(id); atomic_inc(&init_mm.mm_count); current->active_mm = &init_mm; BUG_ON(current->mm); enter_lazy_tlb(&init_mm, current); - memset(idle, 0, sizeof(*idle)); } /* @@ -41,7 +39,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) { static const char *hwcap_str[] = { "esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp", - "edat", "etf3eh", "highgprs", "te" + "edat", "etf3eh", "highgprs", "te", "vx" }; unsigned long n = (unsigned long) v - 1; int i; diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index bebacad4830507a5a310a8722b87985465287f2f..99a567b70d16f6e18a0028b0221f2bb2962f2718 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -38,15 +38,6 @@ #define CREATE_TRACE_POINTS #include -enum s390_regset { - REGSET_GENERAL, - REGSET_FP, - REGSET_LAST_BREAK, - REGSET_TDB, - REGSET_SYSTEM_CALL, - REGSET_GENERAL_EXTENDED, -}; - void update_cr_regs(struct task_struct *task) { struct pt_regs *regs = task_pt_regs(task); @@ -55,27 +46,39 @@ void update_cr_regs(struct task_struct *task) #ifdef CONFIG_64BIT /* Take care of the enable/disable of transactional execution. */ - if (MACHINE_HAS_TE) { + if (MACHINE_HAS_TE || MACHINE_HAS_VX) { unsigned long cr, cr_new; __ctl_store(cr, 0, 0); - /* Set or clear transaction execution TXC bit 8. */ - cr_new = cr | (1UL << 55); - if (task->thread.per_flags & PER_FLAG_NO_TE) - cr_new &= ~(1UL << 55); + cr_new = cr; + if (MACHINE_HAS_TE) { + /* Set or clear transaction execution TXC bit 8. */ + cr_new |= (1UL << 55); + if (task->thread.per_flags & PER_FLAG_NO_TE) + cr_new &= ~(1UL << 55); + } + if (MACHINE_HAS_VX) { + /* Enable/disable of vector extension */ + cr_new &= ~(1UL << 17); + if (task->thread.vxrs) + cr_new |= (1UL << 17); + } if (cr_new != cr) __ctl_load(cr_new, 0, 0); - /* Set or clear transaction execution TDC bits 62 and 63. */ - __ctl_store(cr, 2, 2); - cr_new = cr & ~3UL; - if (task->thread.per_flags & PER_FLAG_TE_ABORT_RAND) { - if (task->thread.per_flags & PER_FLAG_TE_ABORT_RAND_TEND) - cr_new |= 1UL; - else - cr_new |= 2UL; + if (MACHINE_HAS_TE) { + /* Set/clear transaction execution TDC bits 62/63. */ + __ctl_store(cr, 2, 2); + cr_new = cr & ~3UL; + if (task->thread.per_flags & PER_FLAG_TE_ABORT_RAND) { + if (task->thread.per_flags & + PER_FLAG_TE_ABORT_RAND_TEND) + cr_new |= 1UL; + else + cr_new |= 2UL; + } + if (cr_new != cr) + __ctl_load(cr_new, 2, 2); } - if (cr_new != cr) - __ctl_load(cr_new, 2, 2); } #endif /* Copy user specified PER registers */ @@ -84,7 +87,8 @@ void update_cr_regs(struct task_struct *task) new.end = thread->per_user.end; /* merge TIF_SINGLE_STEP into user specified PER registers. */ - if (test_tsk_thread_flag(task, TIF_SINGLE_STEP)) { + if (test_tsk_thread_flag(task, TIF_SINGLE_STEP) || + test_tsk_thread_flag(task, TIF_UPROBE_SINGLESTEP)) { if (test_tsk_thread_flag(task, TIF_BLOCK_STEP)) new.control |= PER_EVENT_BRANCH; else @@ -93,6 +97,8 @@ void update_cr_regs(struct task_struct *task) new.control |= PER_CONTROL_SUSPENSION; new.control |= PER_EVENT_TRANSACTION_END; #endif + if (test_tsk_thread_flag(task, TIF_UPROBE_SINGLESTEP)) + new.control |= PER_EVENT_IFETCH; new.start = 0; new.end = PSW_ADDR_INSN; } @@ -828,9 +834,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_enter(regs, regs->gprs[2]); - audit_syscall_entry(is_compat_task() ? - AUDIT_ARCH_S390 : AUDIT_ARCH_S390X, - regs->gprs[2], regs->orig_gpr2, + audit_syscall_entry(regs->gprs[2], regs->orig_gpr2, regs->gprs[3], regs->gprs[4], regs->gprs[5]); out: @@ -923,7 +927,15 @@ static int s390_fpregs_get(struct task_struct *target, save_fp_ctl(&target->thread.fp_regs.fpc); save_fp_regs(target->thread.fp_regs.fprs); } +#ifdef CONFIG_64BIT + else if (target->thread.vxrs) { + int i; + for (i = 0; i < __NUM_VXRS_LOW; i++) + target->thread.fp_regs.fprs[i] = + *(freg_t *)(target->thread.vxrs + i); + } +#endif return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &target->thread.fp_regs, 0, -1); } @@ -957,9 +969,20 @@ static int s390_fpregs_set(struct task_struct *target, target->thread.fp_regs.fprs, offsetof(s390_fp_regs, fprs), -1); - if (rc == 0 && target == current) { - restore_fp_ctl(&target->thread.fp_regs.fpc); - restore_fp_regs(target->thread.fp_regs.fprs); + if (rc == 0) { + if (target == current) { + restore_fp_ctl(&target->thread.fp_regs.fpc); + restore_fp_regs(target->thread.fp_regs.fprs); + } +#ifdef CONFIG_64BIT + else if (target->thread.vxrs) { + int i; + + for (i = 0; i < __NUM_VXRS_LOW; i++) + *(freg_t *)(target->thread.vxrs + i) = + target->thread.fp_regs.fprs[i]; + } +#endif } return rc; @@ -1015,6 +1038,95 @@ static int s390_tdb_set(struct task_struct *target, return 0; } +static int s390_vxrs_active(struct task_struct *target, + const struct user_regset *regset) +{ + return !!target->thread.vxrs; +} + +static int s390_vxrs_low_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + __u64 vxrs[__NUM_VXRS_LOW]; + int i; + + if (target->thread.vxrs) { + if (target == current) + save_vx_regs(target->thread.vxrs); + for (i = 0; i < __NUM_VXRS_LOW; i++) + vxrs[i] = *((__u64 *)(target->thread.vxrs + i) + 1); + } else + memset(vxrs, 0, sizeof(vxrs)); + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1); +} + +static int s390_vxrs_low_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + __u64 vxrs[__NUM_VXRS_LOW]; + int i, rc; + + if (!target->thread.vxrs) { + rc = alloc_vector_registers(target); + if (rc) + return rc; + } else if (target == current) + save_vx_regs(target->thread.vxrs); + + rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1); + if (rc == 0) { + for (i = 0; i < __NUM_VXRS_LOW; i++) + *((__u64 *)(target->thread.vxrs + i) + 1) = vxrs[i]; + if (target == current) + restore_vx_regs(target->thread.vxrs); + } + + return rc; +} + +static int s390_vxrs_high_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + __vector128 vxrs[__NUM_VXRS_HIGH]; + + if (target->thread.vxrs) { + if (target == current) + save_vx_regs(target->thread.vxrs); + memcpy(vxrs, target->thread.vxrs + __NUM_VXRS_LOW, + sizeof(vxrs)); + } else + memset(vxrs, 0, sizeof(vxrs)); + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1); +} + +static int s390_vxrs_high_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int rc; + + if (!target->thread.vxrs) { + rc = alloc_vector_registers(target); + if (rc) + return rc; + } else if (target == current) + save_vx_regs(target->thread.vxrs); + + rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + target->thread.vxrs + __NUM_VXRS_LOW, 0, -1); + if (rc == 0 && target == current) + restore_vx_regs(target->thread.vxrs); + + return rc; +} + #endif static int s390_system_call_get(struct task_struct *target, @@ -1038,7 +1150,7 @@ static int s390_system_call_set(struct task_struct *target, } static const struct user_regset s390_regsets[] = { - [REGSET_GENERAL] = { + { .core_note_type = NT_PRSTATUS, .n = sizeof(s390_regs) / sizeof(long), .size = sizeof(long), @@ -1046,7 +1158,7 @@ static const struct user_regset s390_regsets[] = { .get = s390_regs_get, .set = s390_regs_set, }, - [REGSET_FP] = { + { .core_note_type = NT_PRFPREG, .n = sizeof(s390_fp_regs) / sizeof(long), .size = sizeof(long), @@ -1054,8 +1166,16 @@ static const struct user_regset s390_regsets[] = { .get = s390_fpregs_get, .set = s390_fpregs_set, }, + { + .core_note_type = NT_S390_SYSTEM_CALL, + .n = 1, + .size = sizeof(unsigned int), + .align = sizeof(unsigned int), + .get = s390_system_call_get, + .set = s390_system_call_set, + }, #ifdef CONFIG_64BIT - [REGSET_LAST_BREAK] = { + { .core_note_type = NT_S390_LAST_BREAK, .n = 1, .size = sizeof(long), @@ -1063,7 +1183,7 @@ static const struct user_regset s390_regsets[] = { .get = s390_last_break_get, .set = s390_last_break_set, }, - [REGSET_TDB] = { + { .core_note_type = NT_S390_TDB, .n = 1, .size = 256, @@ -1071,15 +1191,25 @@ static const struct user_regset s390_regsets[] = { .get = s390_tdb_get, .set = s390_tdb_set, }, -#endif - [REGSET_SYSTEM_CALL] = { - .core_note_type = NT_S390_SYSTEM_CALL, - .n = 1, - .size = sizeof(unsigned int), - .align = sizeof(unsigned int), - .get = s390_system_call_get, - .set = s390_system_call_set, + { + .core_note_type = NT_S390_VXRS_LOW, + .n = __NUM_VXRS_LOW, + .size = sizeof(__u64), + .align = sizeof(__u64), + .active = s390_vxrs_active, + .get = s390_vxrs_low_get, + .set = s390_vxrs_low_set, }, + { + .core_note_type = NT_S390_VXRS_HIGH, + .n = __NUM_VXRS_HIGH, + .size = sizeof(__vector128), + .align = sizeof(__vector128), + .active = s390_vxrs_active, + .get = s390_vxrs_high_get, + .set = s390_vxrs_high_set, + }, +#endif }; static const struct user_regset_view user_s390_view = { @@ -1244,7 +1374,7 @@ static int s390_compat_last_break_set(struct task_struct *target, } static const struct user_regset s390_compat_regsets[] = { - [REGSET_GENERAL] = { + { .core_note_type = NT_PRSTATUS, .n = sizeof(s390_compat_regs) / sizeof(compat_long_t), .size = sizeof(compat_long_t), @@ -1252,7 +1382,7 @@ static const struct user_regset s390_compat_regsets[] = { .get = s390_compat_regs_get, .set = s390_compat_regs_set, }, - [REGSET_FP] = { + { .core_note_type = NT_PRFPREG, .n = sizeof(s390_fp_regs) / sizeof(compat_long_t), .size = sizeof(compat_long_t), @@ -1260,7 +1390,15 @@ static const struct user_regset s390_compat_regsets[] = { .get = s390_fpregs_get, .set = s390_fpregs_set, }, - [REGSET_LAST_BREAK] = { + { + .core_note_type = NT_S390_SYSTEM_CALL, + .n = 1, + .size = sizeof(compat_uint_t), + .align = sizeof(compat_uint_t), + .get = s390_system_call_get, + .set = s390_system_call_set, + }, + { .core_note_type = NT_S390_LAST_BREAK, .n = 1, .size = sizeof(long), @@ -1268,7 +1406,7 @@ static const struct user_regset s390_compat_regsets[] = { .get = s390_compat_last_break_get, .set = s390_compat_last_break_set, }, - [REGSET_TDB] = { + { .core_note_type = NT_S390_TDB, .n = 1, .size = 256, @@ -1276,15 +1414,25 @@ static const struct user_regset s390_compat_regsets[] = { .get = s390_tdb_get, .set = s390_tdb_set, }, - [REGSET_SYSTEM_CALL] = { - .core_note_type = NT_S390_SYSTEM_CALL, - .n = 1, - .size = sizeof(compat_uint_t), - .align = sizeof(compat_uint_t), - .get = s390_system_call_get, - .set = s390_system_call_set, + { + .core_note_type = NT_S390_VXRS_LOW, + .n = __NUM_VXRS_LOW, + .size = sizeof(__u64), + .align = sizeof(__u64), + .active = s390_vxrs_active, + .get = s390_vxrs_low_get, + .set = s390_vxrs_low_set, + }, + { + .core_note_type = NT_S390_VXRS_HIGH, + .n = __NUM_VXRS_HIGH, + .size = sizeof(__vector128), + .align = sizeof(__vector128), + .active = s390_vxrs_active, + .get = s390_vxrs_high_get, + .set = s390_vxrs_high_set, }, - [REGSET_GENERAL_EXTENDED] = { + { .core_note_type = NT_S390_HIGH_GPRS, .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t), .size = sizeof(compat_long_t), diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 82bc113e8c1dd3718cf663a88d7bc9a2c9c97fb4..e80d9ff9a56d41b15055e3270234c4683f7f7d91 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -343,6 +343,9 @@ static void __init setup_lowcore(void) __ctl_set_bit(14, 29); } #else + if (MACHINE_HAS_VX) + lc->vector_save_area_addr = + (unsigned long) &lc->vector_save_area; lc->vdso_per_cpu_data = (unsigned long) &lc->paste[0]; #endif lc->sync_enter_timer = S390_lowcore.sync_enter_timer; @@ -452,8 +455,8 @@ static void __init setup_memory_end(void) #ifdef CONFIG_64BIT vmalloc_size = VMALLOC_END ?: (128UL << 30) - MODULES_LEN; tmp = (memory_end ?: max_physmem_end) / PAGE_SIZE; - tmp = tmp * (sizeof(struct page) + PAGE_SIZE) + vmalloc_size; - if (tmp <= (1UL << 42)) + tmp = tmp * (sizeof(struct page) + PAGE_SIZE); + if (tmp + vmalloc_size + MODULES_LEN <= (1UL << 42)) vmax = 1UL << 42; /* 3-level kernel page table */ else vmax = 1UL << 53; /* 4-level kernel page table */ @@ -765,6 +768,12 @@ static void __init setup_hwcaps(void) */ if (test_facility(50) && test_facility(73)) elf_hwcap |= HWCAP_S390_TE; + + /* + * Vector extension HWCAP_S390_VXRS is bit 11. + */ + if (test_facility(129)) + elf_hwcap |= HWCAP_S390_VXRS; #endif get_cpu_id(&cpu_id); diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index 469c4c6d91822f32c2512551d6c8c29026ff8462..0c1a0ff0a558bda9a389091f86628fb8b0f825bc 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c @@ -31,30 +31,117 @@ #include #include "entry.h" -typedef struct +/* + * Layout of an old-style signal-frame: + * ----------------------------------------- + * | save area (_SIGNAL_FRAMESIZE) | + * ----------------------------------------- + * | struct sigcontext | + * | oldmask | + * | _sigregs * | + * ----------------------------------------- + * | _sigregs with | + * | _s390_regs_common | + * | _s390_fp_regs | + * ----------------------------------------- + * | int signo | + * ----------------------------------------- + * | _sigregs_ext with | + * | gprs_high 64 byte (opt) | + * | vxrs_low 128 byte (opt) | + * | vxrs_high 256 byte (opt) | + * | reserved 128 byte (opt) | + * ----------------------------------------- + * | __u16 svc_insn | + * ----------------------------------------- + * The svc_insn entry with the sigreturn system call opcode does not + * have a fixed position and moves if gprs_high or vxrs exist. + * Future extensions will be added to _sigregs_ext. + */ +struct sigframe { __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; struct sigcontext sc; _sigregs sregs; int signo; - __u8 retcode[S390_SYSCALL_SIZE]; -} sigframe; + _sigregs_ext sregs_ext; + __u16 svc_insn; /* Offset of svc_insn is NOT fixed! */ +}; -typedef struct +/* + * Layout of an rt signal-frame: + * ----------------------------------------- + * | save area (_SIGNAL_FRAMESIZE) | + * ----------------------------------------- + * | svc __NR_rt_sigreturn 2 byte | + * ----------------------------------------- + * | struct siginfo | + * ----------------------------------------- + * | struct ucontext_extended with | + * | unsigned long uc_flags | + * | struct ucontext *uc_link | + * | stack_t uc_stack | + * | _sigregs uc_mcontext with | + * | _s390_regs_common | + * | _s390_fp_regs | + * | sigset_t uc_sigmask | + * | _sigregs_ext uc_mcontext_ext | + * | gprs_high 64 byte (opt) | + * | vxrs_low 128 byte (opt) | + * | vxrs_high 256 byte (opt)| + * | reserved 128 byte (opt) | + * ----------------------------------------- + * Future extensions will be added to _sigregs_ext. + */ +struct rt_sigframe { __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; - __u8 retcode[S390_SYSCALL_SIZE]; + __u16 svc_insn; struct siginfo info; - struct ucontext uc; -} rt_sigframe; + struct ucontext_extended uc; +}; + +/* Store registers needed to create the signal frame */ +static void store_sigregs(void) +{ + save_access_regs(current->thread.acrs); + save_fp_ctl(¤t->thread.fp_regs.fpc); +#ifdef CONFIG_64BIT + if (current->thread.vxrs) { + int i; + + save_vx_regs(current->thread.vxrs); + for (i = 0; i < __NUM_FPRS; i++) + current->thread.fp_regs.fprs[i] = + *(freg_t *)(current->thread.vxrs + i); + } else +#endif + save_fp_regs(current->thread.fp_regs.fprs); +} + +/* Load registers after signal return */ +static void load_sigregs(void) +{ + restore_access_regs(current->thread.acrs); + /* restore_fp_ctl is done in restore_sigregs */ +#ifdef CONFIG_64BIT + if (current->thread.vxrs) { + int i; + + for (i = 0; i < __NUM_FPRS; i++) + *(freg_t *)(current->thread.vxrs + i) = + current->thread.fp_regs.fprs[i]; + restore_vx_regs(current->thread.vxrs); + } else +#endif + restore_fp_regs(current->thread.fp_regs.fprs); +} /* Returns non-zero on fault. */ static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs) { _sigregs user_sregs; - save_access_regs(current->thread.acrs); - /* Copy a 'clean' PSW mask to the user to avoid leaking information about whether PER is currently on. */ user_sregs.regs.psw.mask = PSW_USER_BITS | @@ -63,12 +150,6 @@ static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs) memcpy(&user_sregs.regs.gprs, ®s->gprs, sizeof(sregs->regs.gprs)); memcpy(&user_sregs.regs.acrs, current->thread.acrs, sizeof(user_sregs.regs.acrs)); - /* - * We have to store the fp registers to current->thread.fp_regs - * to merge them with the emulated registers. - */ - save_fp_ctl(¤t->thread.fp_regs.fpc); - save_fp_regs(current->thread.fp_regs.fprs); memcpy(&user_sregs.fpregs, ¤t->thread.fp_regs, sizeof(user_sregs.fpregs)); if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs))) @@ -107,20 +188,64 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs) memcpy(®s->gprs, &user_sregs.regs.gprs, sizeof(sregs->regs.gprs)); memcpy(¤t->thread.acrs, &user_sregs.regs.acrs, sizeof(current->thread.acrs)); - restore_access_regs(current->thread.acrs); memcpy(¤t->thread.fp_regs, &user_sregs.fpregs, sizeof(current->thread.fp_regs)); - restore_fp_regs(current->thread.fp_regs.fprs); clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */ return 0; } +/* Returns non-zero on fault. */ +static int save_sigregs_ext(struct pt_regs *regs, + _sigregs_ext __user *sregs_ext) +{ +#ifdef CONFIG_64BIT + __u64 vxrs[__NUM_VXRS_LOW]; + int i; + + /* Save vector registers to signal stack */ + if (current->thread.vxrs) { + for (i = 0; i < __NUM_VXRS_LOW; i++) + vxrs[i] = *((__u64 *)(current->thread.vxrs + i) + 1); + if (__copy_to_user(&sregs_ext->vxrs_low, vxrs, + sizeof(sregs_ext->vxrs_low)) || + __copy_to_user(&sregs_ext->vxrs_high, + current->thread.vxrs + __NUM_VXRS_LOW, + sizeof(sregs_ext->vxrs_high))) + return -EFAULT; + } +#endif + return 0; +} + +static int restore_sigregs_ext(struct pt_regs *regs, + _sigregs_ext __user *sregs_ext) +{ +#ifdef CONFIG_64BIT + __u64 vxrs[__NUM_VXRS_LOW]; + int i; + + /* Restore vector registers from signal stack */ + if (current->thread.vxrs) { + if (__copy_from_user(vxrs, &sregs_ext->vxrs_low, + sizeof(sregs_ext->vxrs_low)) || + __copy_from_user(current->thread.vxrs + __NUM_VXRS_LOW, + &sregs_ext->vxrs_high, + sizeof(sregs_ext->vxrs_high))) + return -EFAULT; + for (i = 0; i < __NUM_VXRS_LOW; i++) + *((__u64 *)(current->thread.vxrs + i) + 1) = vxrs[i]; + } +#endif + return 0; +} + SYSCALL_DEFINE0(sigreturn) { struct pt_regs *regs = task_pt_regs(current); - sigframe __user *frame = (sigframe __user *)regs->gprs[15]; + struct sigframe __user *frame = + (struct sigframe __user *) regs->gprs[15]; sigset_t set; if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE)) @@ -128,6 +253,9 @@ SYSCALL_DEFINE0(sigreturn) set_current_blocked(&set); if (restore_sigregs(regs, &frame->sregs)) goto badframe; + if (restore_sigregs_ext(regs, &frame->sregs_ext)) + goto badframe; + load_sigregs(); return regs->gprs[2]; badframe: force_sig(SIGSEGV, current); @@ -137,27 +265,26 @@ SYSCALL_DEFINE0(sigreturn) SYSCALL_DEFINE0(rt_sigreturn) { struct pt_regs *regs = task_pt_regs(current); - rt_sigframe __user *frame = (rt_sigframe __user *)regs->gprs[15]; + struct rt_sigframe __user *frame = + (struct rt_sigframe __user *)regs->gprs[15]; sigset_t set; if (__copy_from_user(&set.sig, &frame->uc.uc_sigmask, sizeof(set))) goto badframe; set_current_blocked(&set); + if (restore_altstack(&frame->uc.uc_stack)) + goto badframe; if (restore_sigregs(regs, &frame->uc.uc_mcontext)) goto badframe; - if (restore_altstack(&frame->uc.uc_stack)) + if (restore_sigregs_ext(regs, &frame->uc.uc_mcontext_ext)) goto badframe; + load_sigregs(); return regs->gprs[2]; badframe: force_sig(SIGSEGV, current); return 0; } -/* - * Set up a signal frame. - */ - - /* * Determine which stack to use.. */ @@ -195,39 +322,63 @@ static inline int map_signal(int sig) static int setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs * regs) { - sigframe __user *frame; - - frame = get_sigframe(ka, regs, sizeof(sigframe)); + struct sigframe __user *frame; + struct sigcontext sc; + unsigned long restorer; + size_t frame_size; + /* + * gprs_high are only present for a 31-bit task running on + * a 64-bit kernel (see compat_signal.c) but the space for + * gprs_high need to be allocated if vector registers are + * included in the signal frame on a 31-bit system. + */ + frame_size = sizeof(*frame) - sizeof(frame->sregs_ext); + if (MACHINE_HAS_VX) + frame_size += sizeof(frame->sregs_ext); + frame = get_sigframe(ka, regs, frame_size); if (frame == (void __user *) -1UL) return -EFAULT; - if (__copy_to_user(&frame->sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE)) + /* Set up backchain. */ + if (__put_user(regs->gprs[15], (addr_t __user *) frame)) return -EFAULT; + /* Create struct sigcontext on the signal stack */ + memcpy(&sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE); + sc.sregs = (_sigregs __user __force *) &frame->sregs; + if (__copy_to_user(&frame->sc, &sc, sizeof(frame->sc))) + return -EFAULT; + + /* Store registers needed to create the signal frame */ + store_sigregs(); + + /* Create _sigregs on the signal stack */ if (save_sigregs(regs, &frame->sregs)) return -EFAULT; - if (__put_user(&frame->sregs, &frame->sc.sregs)) + + /* Place signal number on stack to allow backtrace from handler. */ + if (__put_user(regs->gprs[2], (int __user *) &frame->signo)) + return -EFAULT; + + /* Create _sigregs_ext on the signal stack */ + if (save_sigregs_ext(regs, &frame->sregs_ext)) return -EFAULT; /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa.sa_flags & SA_RESTORER) { - regs->gprs[14] = (unsigned long) - ka->sa.sa_restorer | PSW_ADDR_AMODE; + restorer = (unsigned long) ka->sa.sa_restorer | PSW_ADDR_AMODE; } else { - regs->gprs[14] = (unsigned long) - frame->retcode | PSW_ADDR_AMODE; - if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, - (u16 __user *)(frame->retcode))) + /* Signal frame without vector registers are short ! */ + __u16 __user *svc = (void *) frame + frame_size - 2; + if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, svc)) return -EFAULT; + restorer = (unsigned long) svc | PSW_ADDR_AMODE; } - /* Set up backchain. */ - if (__put_user(regs->gprs[15], (addr_t __user *) frame)) - return -EFAULT; - /* Set up registers for signal handler */ + regs->gprs[14] = restorer; regs->gprs[15] = (unsigned long) frame; /* Force default amode and default user address space control. */ regs->psw.mask = PSW_MASK_EA | PSW_MASK_BA | @@ -247,54 +398,69 @@ static int setup_frame(int sig, struct k_sigaction *ka, regs->gprs[5] = regs->int_parm_long; regs->gprs[6] = task_thread_info(current)->last_break; } - - /* Place signal number on stack to allow backtrace from handler. */ - if (__put_user(regs->gprs[2], (int __user *) &frame->signo)) - return -EFAULT; return 0; } static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { - int err = 0; - rt_sigframe __user *frame; - - frame = get_sigframe(&ksig->ka, regs, sizeof(rt_sigframe)); + struct rt_sigframe __user *frame; + unsigned long uc_flags, restorer; + size_t frame_size; + frame_size = sizeof(struct rt_sigframe) - sizeof(_sigregs_ext); + /* + * gprs_high are only present for a 31-bit task running on + * a 64-bit kernel (see compat_signal.c) but the space for + * gprs_high need to be allocated if vector registers are + * included in the signal frame on a 31-bit system. + */ + uc_flags = 0; +#ifdef CONFIG_64BIT + if (MACHINE_HAS_VX) { + frame_size += sizeof(_sigregs_ext); + if (current->thread.vxrs) + uc_flags |= UC_VXRS; + } +#endif + frame = get_sigframe(&ksig->ka, regs, frame_size); if (frame == (void __user *) -1UL) return -EFAULT; - if (copy_siginfo_to_user(&frame->info, &ksig->info)) - return -EFAULT; - - /* Create the ucontext. */ - err |= __put_user(0, &frame->uc.uc_flags); - err |= __put_user(NULL, &frame->uc.uc_link); - err |= __save_altstack(&frame->uc.uc_stack, regs->gprs[15]); - err |= save_sigregs(regs, &frame->uc.uc_mcontext); - err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); - if (err) + /* Set up backchain. */ + if (__put_user(regs->gprs[15], (addr_t __user *) frame)) return -EFAULT; /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ksig->ka.sa.sa_flags & SA_RESTORER) { - regs->gprs[14] = (unsigned long) + restorer = (unsigned long) ksig->ka.sa.sa_restorer | PSW_ADDR_AMODE; } else { - regs->gprs[14] = (unsigned long) - frame->retcode | PSW_ADDR_AMODE; - if (__put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, - (u16 __user *)(frame->retcode))) + __u16 __user *svc = &frame->svc_insn; + if (__put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, svc)) return -EFAULT; + restorer = (unsigned long) svc | PSW_ADDR_AMODE; } - /* Set up backchain. */ - if (__put_user(regs->gprs[15], (addr_t __user *) frame)) + /* Create siginfo on the signal stack */ + if (copy_siginfo_to_user(&frame->info, &ksig->info)) + return -EFAULT; + + /* Store registers needed to create the signal frame */ + store_sigregs(); + + /* Create ucontext on the signal stack. */ + if (__put_user(uc_flags, &frame->uc.uc_flags) || + __put_user(NULL, &frame->uc.uc_link) || + __save_altstack(&frame->uc.uc_stack, regs->gprs[15]) || + save_sigregs(regs, &frame->uc.uc_mcontext) || + __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)) || + save_sigregs_ext(regs, &frame->uc.uc_mcontext_ext)) return -EFAULT; /* Set up registers for signal handler */ + regs->gprs[14] = restorer; regs->gprs[15] = (unsigned long) frame; /* Force default amode and default user address space control. */ regs->psw.mask = PSW_MASK_EA | PSW_MASK_BA | diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 243c7e51260055c3de2a57e2852ed82de075f18f..6fd9e60101f1733cc3c84f010657561d4c895985 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -45,6 +45,7 @@ #include #include #include +#include #include "entry.h" enum { @@ -82,7 +83,8 @@ DEFINE_MUTEX(smp_cpu_state_mutex); /* * Signal processor helper functions. */ -static inline int __pcpu_sigp_relax(u16 addr, u8 order, u32 parm, u32 *status) +static inline int __pcpu_sigp_relax(u16 addr, u8 order, unsigned long parm, + u32 *status) { int cc; @@ -178,6 +180,9 @@ static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu) goto out; } #else + if (MACHINE_HAS_VX) + lc->vector_save_area_addr = + (unsigned long) &lc->vector_save_area; if (vdso_alloc_per_cpu(lc)) goto out; #endif @@ -333,12 +338,6 @@ int smp_vcpu_scheduled(int cpu) return pcpu_running(pcpu_devices + cpu); } -void smp_yield(void) -{ - if (MACHINE_HAS_DIAG44) - asm volatile("diag 0,0,0x44"); -} - void smp_yield_cpu(int cpu) { if (MACHINE_HAS_DIAG9C) @@ -517,35 +516,53 @@ EXPORT_SYMBOL(smp_ctl_clear_bit); static void __init smp_get_save_area(int cpu, u16 address) { void *lc = pcpu_devices[0].lowcore; - struct save_area *save_area; + struct save_area_ext *sa_ext; + unsigned long vx_sa; if (is_kdump_kernel()) return; if (!OLDMEM_BASE && (address == boot_cpu_address || ipl_info.type != IPL_TYPE_FCP_DUMP)) return; - save_area = dump_save_area_create(cpu); - if (!save_area) + sa_ext = dump_save_area_create(cpu); + if (!sa_ext) panic("could not allocate memory for save area\n"); if (address == boot_cpu_address) { /* Copy the registers of the boot cpu. */ - copy_oldmem_page(1, (void *) save_area, sizeof(*save_area), + copy_oldmem_page(1, (void *) &sa_ext->sa, sizeof(sa_ext->sa), SAVE_AREA_BASE - PAGE_SIZE, 0); + if (MACHINE_HAS_VX) + save_vx_regs_safe(sa_ext->vx_regs); return; } /* Get the registers of a non-boot cpu. */ __pcpu_sigp_relax(address, SIGP_STOP_AND_STORE_STATUS, 0, NULL); - memcpy_real(save_area, lc + SAVE_AREA_BASE, sizeof(*save_area)); + memcpy_real(&sa_ext->sa, lc + SAVE_AREA_BASE, sizeof(sa_ext->sa)); + if (!MACHINE_HAS_VX) + return; + /* Get the VX registers */ + vx_sa = __get_free_page(GFP_KERNEL); + if (!vx_sa) + panic("could not allocate memory for VX save area\n"); + __pcpu_sigp_relax(address, SIGP_STORE_ADDITIONAL_STATUS, vx_sa, NULL); + memcpy(sa_ext->vx_regs, (void *) vx_sa, sizeof(sa_ext->vx_regs)); + free_page(vx_sa); } int smp_store_status(int cpu) { + unsigned long vx_sa; struct pcpu *pcpu; pcpu = pcpu_devices + cpu; if (__pcpu_sigp_relax(pcpu->address, SIGP_STOP_AND_STORE_STATUS, 0, NULL) != SIGP_CC_ORDER_CODE_ACCEPTED) return -EIO; + if (!MACHINE_HAS_VX) + return 0; + vx_sa = __pa(pcpu->lowcore->vector_save_area_addr); + __pcpu_sigp_relax(pcpu->address, SIGP_STORE_ADDITIONAL_STATUS, + vx_sa, NULL); return 0; } @@ -667,7 +684,7 @@ static void smp_start_secondary(void *cpuvoid) cpu_init(); preempt_disable(); init_cpu_timer(); - init_cpu_vtimer(); + vtime_init(); pfault_init(); notify_cpu_starting(smp_processor_id()); set_cpu_online(smp_processor_id(), true); @@ -726,6 +743,7 @@ int __cpu_disable(void) cregs[6] &= ~0xff000000UL; /* disable all I/O interrupts */ cregs[14] &= ~0x1f000000UL; /* disable most machine checks */ __ctl_load(cregs, 0, 15); + clear_cpu_flag(CIF_NOHZ_DELAY); return 0; } @@ -898,42 +916,6 @@ static struct attribute_group cpu_common_attr_group = { .attrs = cpu_common_attrs, }; -static ssize_t show_idle_count(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); - unsigned long long idle_count; - unsigned int sequence; - - do { - sequence = ACCESS_ONCE(idle->sequence); - idle_count = ACCESS_ONCE(idle->idle_count); - if (ACCESS_ONCE(idle->clock_idle_enter)) - idle_count++; - } while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence)); - return sprintf(buf, "%llu\n", idle_count); -} -static DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL); - -static ssize_t show_idle_time(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); - unsigned long long now, idle_time, idle_enter, idle_exit; - unsigned int sequence; - - do { - now = get_tod_clock(); - sequence = ACCESS_ONCE(idle->sequence); - idle_time = ACCESS_ONCE(idle->idle_time); - idle_enter = ACCESS_ONCE(idle->clock_idle_enter); - idle_exit = ACCESS_ONCE(idle->clock_idle_exit); - } while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence)); - idle_time += idle_enter ? ((idle_exit ? : now) - idle_enter) : 0; - return sprintf(buf, "%llu\n", idle_time >> 12); -} -static DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL); - static struct attribute *cpu_online_attrs[] = { &dev_attr_idle_count.attr, &dev_attr_idle_time_us.attr, diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 4cef607f371122fc2fcc7959bd6f12cff3d3d896..005d665fe4a5290eedca6e7d2177bed687332920 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -92,7 +92,7 @@ void clock_comparator_work(void) struct clock_event_device *cd; S390_lowcore.clock_comparator = -1ULL; - cd = &__get_cpu_var(comparators); + cd = this_cpu_ptr(&comparators); cd->event_handler(cd); } @@ -232,6 +232,19 @@ void update_vsyscall(struct timekeeper *tk) vdso_data->wtom_clock_nsec -= nsecps; vdso_data->wtom_clock_sec++; } + + vdso_data->xtime_coarse_sec = tk->xtime_sec; + vdso_data->xtime_coarse_nsec = + (long)(tk->tkr.xtime_nsec >> tk->tkr.shift); + vdso_data->wtom_coarse_sec = + vdso_data->xtime_coarse_sec + tk->wall_to_monotonic.tv_sec; + vdso_data->wtom_coarse_nsec = + vdso_data->xtime_coarse_nsec + tk->wall_to_monotonic.tv_nsec; + while (vdso_data->wtom_coarse_nsec >= NSEC_PER_SEC) { + vdso_data->wtom_coarse_nsec -= NSEC_PER_SEC; + vdso_data->wtom_coarse_sec++; + } + vdso_data->tk_mult = tk->tkr.mult; vdso_data->tk_shift = tk->tkr.shift; smp_wmb(); @@ -360,7 +373,7 @@ EXPORT_SYMBOL(get_sync_clock); */ static void disable_sync_clock(void *dummy) { - atomic_t *sw_ptr = &__get_cpu_var(clock_sync_word); + atomic_t *sw_ptr = this_cpu_ptr(&clock_sync_word); /* * Clear the in-sync bit 2^31. All get_sync_clock calls will * fail until the sync bit is turned back on. In addition @@ -377,7 +390,7 @@ static void disable_sync_clock(void *dummy) */ static void enable_sync_clock(void) { - atomic_t *sw_ptr = &__get_cpu_var(clock_sync_word); + atomic_t *sw_ptr = this_cpu_ptr(&clock_sync_word); atomic_set_mask(0x80000000, sw_ptr); } diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c index 355a16c557026a21f50fbb39b51a5f1320b0773a..b93bed76ea94026d45c782eebe75c08dfb1a323d 100644 --- a/arch/s390/kernel/topology.c +++ b/arch/s390/kernel/topology.c @@ -464,15 +464,17 @@ static struct sched_domain_topology_level s390_topology[] = { static int __init topology_init(void) { - if (!MACHINE_HAS_TOPOLOGY) { + if (MACHINE_HAS_TOPOLOGY) + set_topology_timer(); + else topology_update_polarization_simple(); - goto out; - } - set_topology_timer(); -out: - - set_sched_topology(s390_topology); - return device_create_file(cpu_subsys.dev_root, &dev_attr_dispatching); } device_initcall(topology_init); + +static int __init early_topology_init(void) +{ + set_sched_topology(s390_topology); + return 0; +} +early_initcall(early_topology_init); diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index c5762324d9ee435367314d8fa2f03e32e2365f0e..9ff5ecba26aba803f3d8ad24196f61409bc99e2a 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include "entry.h" int show_unhandled_signals = 1; @@ -58,15 +60,10 @@ int is_valid_bugaddr(unsigned long addr) return 1; } -static void __kprobes do_trap(struct pt_regs *regs, - int si_signo, int si_code, char *str) +void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str) { siginfo_t info; - if (notify_die(DIE_TRAP, str, regs, 0, - regs->int_code, si_signo) == NOTIFY_STOP) - return; - if (user_mode(regs)) { info.si_signo = si_signo; info.si_errno = 0; @@ -90,6 +87,15 @@ static void __kprobes do_trap(struct pt_regs *regs, } } +static void __kprobes do_trap(struct pt_regs *regs, int si_signo, int si_code, + char *str) +{ + if (notify_die(DIE_TRAP, str, regs, 0, + regs->int_code, si_signo) == NOTIFY_STOP) + return; + do_report_trap(regs, si_signo, si_code, str); +} + void __kprobes do_per_trap(struct pt_regs *regs) { siginfo_t info; @@ -178,6 +184,7 @@ void __kprobes illegal_op(struct pt_regs *regs) siginfo_t info; __u8 opcode[6]; __u16 __user *location; + int is_uprobe_insn = 0; int signal = 0; location = get_trap_ip(regs); @@ -194,6 +201,10 @@ void __kprobes illegal_op(struct pt_regs *regs) force_sig_info(SIGTRAP, &info, current); } else signal = SIGILL; +#ifdef CONFIG_UPROBES + } else if (*((__u16 *) opcode) == UPROBE_SWBP_INSN) { + is_uprobe_insn = 1; +#endif #ifdef CONFIG_MATHEMU } else if (opcode[0] == 0xb3) { if (get_user(*((__u16 *) (opcode+2)), location+1)) @@ -219,11 +230,13 @@ void __kprobes illegal_op(struct pt_regs *regs) #endif } else signal = SIGILL; - } else { - /* - * If we get an illegal op in kernel mode, send it through the - * kprobes notifier. If kprobes doesn't pick it up, SIGILL - */ + } + /* + * We got either an illegal op in kernel mode, or user space trapped + * on a uprobes illegal instruction. See if kprobes or uprobes picks + * it up. If not, SIGILL. + */ + if (is_uprobe_insn || !user_mode(regs)) { if (notify_die(DIE_BPT, "bpt", regs, 0, 3, SIGTRAP) != NOTIFY_STOP) signal = SIGILL; @@ -292,6 +305,74 @@ DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN, "specification exception"); #endif +#ifdef CONFIG_64BIT +int alloc_vector_registers(struct task_struct *tsk) +{ + __vector128 *vxrs; + int i; + + /* Allocate vector register save area. */ + vxrs = kzalloc(sizeof(__vector128) * __NUM_VXRS, + GFP_KERNEL|__GFP_REPEAT); + if (!vxrs) + return -ENOMEM; + preempt_disable(); + if (tsk == current) + save_fp_regs(tsk->thread.fp_regs.fprs); + /* Copy the 16 floating point registers */ + for (i = 0; i < 16; i++) + *(freg_t *) &vxrs[i] = tsk->thread.fp_regs.fprs[i]; + tsk->thread.vxrs = vxrs; + if (tsk == current) { + __ctl_set_bit(0, 17); + restore_vx_regs(vxrs); + } + preempt_enable(); + return 0; +} + +void vector_exception(struct pt_regs *regs) +{ + int si_code, vic; + + if (!MACHINE_HAS_VX) { + do_trap(regs, SIGILL, ILL_ILLOPN, "illegal operation"); + return; + } + + /* get vector interrupt code from fpc */ + asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc)); + vic = (current->thread.fp_regs.fpc & 0xf00) >> 8; + switch (vic) { + case 1: /* invalid vector operation */ + si_code = FPE_FLTINV; + break; + case 2: /* division by zero */ + si_code = FPE_FLTDIV; + break; + case 3: /* overflow */ + si_code = FPE_FLTOVF; + break; + case 4: /* underflow */ + si_code = FPE_FLTUND; + break; + case 5: /* inexact */ + si_code = FPE_FLTRES; + break; + default: /* unknown cause */ + si_code = 0; + } + do_trap(regs, SIGFPE, si_code, "vector exception"); +} + +static int __init disable_vector_extension(char *str) +{ + S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX; + return 1; +} +__setup("novx", disable_vector_extension); +#endif + void data_exception(struct pt_regs *regs) { __u16 __user *location; @@ -357,6 +438,18 @@ void data_exception(struct pt_regs *regs) } } #endif +#ifdef CONFIG_64BIT + /* Check for vector register enablement */ + if (MACHINE_HAS_VX && !current->thread.vxrs && + (current->thread.fp_regs.fpc & FPC_DXC_MASK) == 0xfe00) { + alloc_vector_registers(current); + /* Vector data exception is suppressing, rewind psw. */ + regs->psw.addr = __rewind_psw(regs->psw, regs->int_code >> 16); + clear_pt_regs_flag(regs, PIF_PER_TRAP); + return; + } +#endif + if (current->thread.fp_regs.fpc & FPC_DXC_MASK) signal = SIGFPE; else diff --git a/arch/s390/kernel/uprobes.c b/arch/s390/kernel/uprobes.c new file mode 100644 index 0000000000000000000000000000000000000000..956f4f7a591c18b1cd6f03129e0c0bc46750a7b6 --- /dev/null +++ b/arch/s390/kernel/uprobes.c @@ -0,0 +1,332 @@ +/* + * User-space Probes (UProbes) for s390 + * + * Copyright IBM Corp. 2014 + * Author(s): Jan Willeke, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "entry.h" + +#define UPROBE_TRAP_NR UINT_MAX + +int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, + unsigned long addr) +{ + return probe_is_prohibited_opcode(auprobe->insn); +} + +int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + if (psw_bits(regs->psw).eaba == PSW_AMODE_24BIT) + return -EINVAL; + if (!is_compat_task() && psw_bits(regs->psw).eaba == PSW_AMODE_31BIT) + return -EINVAL; + clear_pt_regs_flag(regs, PIF_PER_TRAP); + auprobe->saved_per = psw_bits(regs->psw).r; + auprobe->saved_int_code = regs->int_code; + regs->int_code = UPROBE_TRAP_NR; + regs->psw.addr = current->utask->xol_vaddr; + set_tsk_thread_flag(current, TIF_UPROBE_SINGLESTEP); + update_cr_regs(current); + return 0; +} + +bool arch_uprobe_xol_was_trapped(struct task_struct *tsk) +{ + struct pt_regs *regs = task_pt_regs(tsk); + + if (regs->int_code != UPROBE_TRAP_NR) + return true; + return false; +} + +int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + int fixup = probe_get_fixup_type(auprobe->insn); + struct uprobe_task *utask = current->utask; + + clear_tsk_thread_flag(current, TIF_UPROBE_SINGLESTEP); + update_cr_regs(current); + psw_bits(regs->psw).r = auprobe->saved_per; + regs->int_code = auprobe->saved_int_code; + + if (fixup & FIXUP_PSW_NORMAL) + regs->psw.addr += utask->vaddr - utask->xol_vaddr; + if (fixup & FIXUP_RETURN_REGISTER) { + int reg = (auprobe->insn[0] & 0xf0) >> 4; + + regs->gprs[reg] += utask->vaddr - utask->xol_vaddr; + } + if (fixup & FIXUP_BRANCH_NOT_TAKEN) { + int ilen = insn_length(auprobe->insn[0] >> 8); + + if (regs->psw.addr - utask->xol_vaddr == ilen) + regs->psw.addr = utask->vaddr + ilen; + } + /* If per tracing was active generate trap */ + if (regs->psw.mask & PSW_MASK_PER) + do_per_trap(regs); + return 0; +} + +int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, + void *data) +{ + struct die_args *args = data; + struct pt_regs *regs = args->regs; + + if (!user_mode(regs)) + return NOTIFY_DONE; + if (regs->int_code & 0x200) /* Trap during transaction */ + return NOTIFY_DONE; + switch (val) { + case DIE_BPT: + if (uprobe_pre_sstep_notifier(regs)) + return NOTIFY_STOP; + break; + case DIE_SSTEP: + if (uprobe_post_sstep_notifier(regs)) + return NOTIFY_STOP; + default: + break; + } + return NOTIFY_DONE; +} + +void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + clear_thread_flag(TIF_UPROBE_SINGLESTEP); + regs->int_code = auprobe->saved_int_code; + regs->psw.addr = current->utask->vaddr; +} + +unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline, + struct pt_regs *regs) +{ + unsigned long orig; + + orig = regs->gprs[14]; + regs->gprs[14] = trampoline; + return orig; +} + +/* Instruction Emulation */ + +static void adjust_psw_addr(psw_t *psw, unsigned long len) +{ + psw->addr = __rewind_psw(*psw, -len); +} + +#define EMU_ILLEGAL_OP 1 +#define EMU_SPECIFICATION 2 +#define EMU_ADDRESSING 3 + +#define emu_load_ril(ptr, output) \ +({ \ + unsigned int mask = sizeof(*(ptr)) - 1; \ + __typeof__(*(ptr)) input; \ + int __rc = 0; \ + \ + if (!test_facility(34)) \ + __rc = EMU_ILLEGAL_OP; \ + else if ((u64 __force)ptr & mask) \ + __rc = EMU_SPECIFICATION; \ + else if (get_user(input, ptr)) \ + __rc = EMU_ADDRESSING; \ + else \ + *(output) = input; \ + __rc; \ +}) + +#define emu_store_ril(ptr, input) \ +({ \ + unsigned int mask = sizeof(*(ptr)) - 1; \ + int __rc = 0; \ + \ + if (!test_facility(34)) \ + __rc = EMU_ILLEGAL_OP; \ + else if ((u64 __force)ptr & mask) \ + __rc = EMU_SPECIFICATION; \ + else if (put_user(*(input), ptr)) \ + __rc = EMU_ADDRESSING; \ + __rc; \ +}) + +#define emu_cmp_ril(regs, ptr, cmp) \ +({ \ + unsigned int mask = sizeof(*(ptr)) - 1; \ + __typeof__(*(ptr)) input; \ + int __rc = 0; \ + \ + if (!test_facility(34)) \ + __rc = EMU_ILLEGAL_OP; \ + else if ((u64 __force)ptr & mask) \ + __rc = EMU_SPECIFICATION; \ + else if (get_user(input, ptr)) \ + __rc = EMU_ADDRESSING; \ + else if (input > *(cmp)) \ + psw_bits((regs)->psw).cc = 1; \ + else if (input < *(cmp)) \ + psw_bits((regs)->psw).cc = 2; \ + else \ + psw_bits((regs)->psw).cc = 0; \ + __rc; \ +}) + +struct insn_ril { + u8 opc0; + u8 reg : 4; + u8 opc1 : 4; + s32 disp; +} __packed; + +union split_register { + u64 u64; + u32 u32[2]; + u16 u16[4]; + s64 s64; + s32 s32[2]; + s16 s16[4]; +}; + +/* + * pc relative instructions are emulated, since parameters may not be + * accessible from the xol area due to range limitations. + */ +static void handle_insn_ril(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + union split_register *rx; + struct insn_ril *insn; + unsigned int ilen; + void *uptr; + int rc = 0; + + insn = (struct insn_ril *) &auprobe->insn; + rx = (union split_register *) ®s->gprs[insn->reg]; + uptr = (void *)(regs->psw.addr + (insn->disp * 2)); + ilen = insn_length(insn->opc0); + + switch (insn->opc0) { + case 0xc0: + switch (insn->opc1) { + case 0x00: /* larl */ + rx->u64 = (unsigned long)uptr; + break; + } + break; + case 0xc4: + switch (insn->opc1) { + case 0x02: /* llhrl */ + rc = emu_load_ril((u16 __user *)uptr, &rx->u32[1]); + break; + case 0x04: /* lghrl */ + rc = emu_load_ril((s16 __user *)uptr, &rx->u64); + break; + case 0x05: /* lhrl */ + rc = emu_load_ril((s16 __user *)uptr, &rx->u32[1]); + break; + case 0x06: /* llghrl */ + rc = emu_load_ril((u16 __user *)uptr, &rx->u64); + break; + case 0x08: /* lgrl */ + rc = emu_load_ril((u64 __user *)uptr, &rx->u64); + break; + case 0x0c: /* lgfrl */ + rc = emu_load_ril((s32 __user *)uptr, &rx->u64); + break; + case 0x0d: /* lrl */ + rc = emu_load_ril((u32 __user *)uptr, &rx->u32[1]); + break; + case 0x0e: /* llgfrl */ + rc = emu_load_ril((u32 __user *)uptr, &rx->u64); + break; + case 0x07: /* sthrl */ + rc = emu_store_ril((u16 __user *)uptr, &rx->u16[3]); + break; + case 0x0b: /* stgrl */ + rc = emu_store_ril((u64 __user *)uptr, &rx->u64); + break; + case 0x0f: /* strl */ + rc = emu_store_ril((u32 __user *)uptr, &rx->u32[1]); + break; + } + break; + case 0xc6: + switch (insn->opc1) { + case 0x02: /* pfdrl */ + if (!test_facility(34)) + rc = EMU_ILLEGAL_OP; + break; + case 0x04: /* cghrl */ + rc = emu_cmp_ril(regs, (s16 __user *)uptr, &rx->s64); + break; + case 0x05: /* chrl */ + rc = emu_cmp_ril(regs, (s16 __user *)uptr, &rx->s32[1]); + break; + case 0x06: /* clghrl */ + rc = emu_cmp_ril(regs, (u16 __user *)uptr, &rx->u64); + break; + case 0x07: /* clhrl */ + rc = emu_cmp_ril(regs, (u16 __user *)uptr, &rx->u32[1]); + break; + case 0x08: /* cgrl */ + rc = emu_cmp_ril(regs, (s64 __user *)uptr, &rx->s64); + break; + case 0x0a: /* clgrl */ + rc = emu_cmp_ril(regs, (u64 __user *)uptr, &rx->u64); + break; + case 0x0c: /* cgfrl */ + rc = emu_cmp_ril(regs, (s32 __user *)uptr, &rx->s64); + break; + case 0x0d: /* crl */ + rc = emu_cmp_ril(regs, (s32 __user *)uptr, &rx->s32[1]); + break; + case 0x0e: /* clgfrl */ + rc = emu_cmp_ril(regs, (u32 __user *)uptr, &rx->u64); + break; + case 0x0f: /* clrl */ + rc = emu_cmp_ril(regs, (u32 __user *)uptr, &rx->u32[1]); + break; + } + break; + } + adjust_psw_addr(®s->psw, ilen); + switch (rc) { + case EMU_ILLEGAL_OP: + regs->int_code = ilen << 16 | 0x0001; + do_report_trap(regs, SIGILL, ILL_ILLOPC, NULL); + break; + case EMU_SPECIFICATION: + regs->int_code = ilen << 16 | 0x0006; + do_report_trap(regs, SIGILL, ILL_ILLOPC , NULL); + break; + case EMU_ADDRESSING: + regs->int_code = ilen << 16 | 0x0005; + do_report_trap(regs, SIGSEGV, SEGV_MAPERR, NULL); + break; + } +} + +bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + if ((psw_bits(regs->psw).eaba == PSW_AMODE_24BIT) || + ((psw_bits(regs->psw).eaba == PSW_AMODE_31BIT) && + !is_compat_task())) { + regs->psw.addr = __rewind_psw(regs->psw, UPROBE_SWBP_INSN_SIZE); + do_report_trap(regs, SIGILL, ILL_ILLADR, NULL); + return true; + } + if (probe_is_insn_relative_long(auprobe->insn)) { + handle_insn_ril(auprobe, regs); + return true; + } + return false; +} diff --git a/arch/s390/kernel/vdso32/clock_getres.S b/arch/s390/kernel/vdso32/clock_getres.S index 36aaa25d05daa4e1d785103d85a2bb5a078fc2cc..eca3f001f081309c88372de459a6dfcccd754e7d 100644 --- a/arch/s390/kernel/vdso32/clock_getres.S +++ b/arch/s390/kernel/vdso32/clock_getres.S @@ -19,14 +19,20 @@ .type __kernel_clock_getres,@function __kernel_clock_getres: .cfi_startproc + basr %r1,0 + la %r1,4f-.(%r1) chi %r2,__CLOCK_REALTIME je 0f chi %r2,__CLOCK_MONOTONIC + je 0f + la %r1,5f-4f(%r1) + chi %r2,__CLOCK_REALTIME_COARSE + je 0f + chi %r2,__CLOCK_MONOTONIC_COARSE jne 3f 0: ltr %r3,%r3 jz 2f /* res == NULL */ - basr %r1,0 -1: l %r0,4f-1b(%r1) +1: l %r0,0(%r1) xc 0(4,%r3),0(%r3) /* set tp->tv_sec to zero */ st %r0,4(%r3) /* store tp->tv_usec */ 2: lhi %r2,0 @@ -35,5 +41,6 @@ __kernel_clock_getres: svc 0 br %r14 4: .long __CLOCK_REALTIME_RES +5: .long __CLOCK_COARSE_RES .cfi_endproc .size __kernel_clock_getres,.-__kernel_clock_getres diff --git a/arch/s390/kernel/vdso32/clock_gettime.S b/arch/s390/kernel/vdso32/clock_gettime.S index 7cf18f8d4cb4fc9d281169ed8dee675f4c68b6be..48c2206a39561ea9373bb3fb8ec48297b3509867 100644 --- a/arch/s390/kernel/vdso32/clock_gettime.S +++ b/arch/s390/kernel/vdso32/clock_gettime.S @@ -21,8 +21,12 @@ __kernel_clock_gettime: .cfi_startproc basr %r5,0 0: al %r5,21f-0b(%r5) /* get &_vdso_data */ + chi %r2,__CLOCK_REALTIME_COARSE + je 10f chi %r2,__CLOCK_REALTIME je 11f + chi %r2,__CLOCK_MONOTONIC_COARSE + je 9f chi %r2,__CLOCK_MONOTONIC jne 19f @@ -30,8 +34,8 @@ __kernel_clock_gettime: 1: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */ tml %r4,0x0001 /* pending update ? loop */ jnz 1b - stck 24(%r15) /* Store TOD clock */ - lm %r0,%r1,24(%r15) + stcke 24(%r15) /* Store TOD clock */ + lm %r0,%r1,25(%r15) s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ sl %r1,__VDSO_XTIME_STAMP+4(%r5) brc 3,2f @@ -68,12 +72,32 @@ __kernel_clock_gettime: lhi %r2,0 br %r14 + /* CLOCK_MONOTONIC_COARSE */ +9: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */ + tml %r4,0x0001 /* pending update ? loop */ + jnz 9b + l %r2,__VDSO_WTOM_CRS_SEC+4(%r5) + l %r1,__VDSO_WTOM_CRS_NSEC+4(%r5) + cl %r4,__VDSO_UPD_COUNT+4(%r5) /* check update counter */ + jne 9b + j 8b + + /* CLOCK_REALTIME_COARSE */ +10: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */ + tml %r4,0x0001 /* pending update ? loop */ + jnz 10b + l %r2,__VDSO_XTIME_CRS_SEC+4(%r5) + l %r1,__VDSO_XTIME_CRS_NSEC+4(%r5) + cl %r4,__VDSO_UPD_COUNT+4(%r5) /* check update counter */ + jne 10b + j 17f + /* CLOCK_REALTIME */ 11: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */ tml %r4,0x0001 /* pending update ? loop */ jnz 11b - stck 24(%r15) /* Store TOD clock */ - lm %r0,%r1,24(%r15) + stcke 24(%r15) /* Store TOD clock */ + lm %r0,%r1,25(%r15) s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ sl %r1,__VDSO_XTIME_STAMP+4(%r5) brc 3,12f diff --git a/arch/s390/kernel/vdso32/gettimeofday.S b/arch/s390/kernel/vdso32/gettimeofday.S index fd621a950f7c70e917ea6ca44bb4a5fd5b9869ab..60def5f562db532e4c014687e38dcefdf1344383 100644 --- a/arch/s390/kernel/vdso32/gettimeofday.S +++ b/arch/s390/kernel/vdso32/gettimeofday.S @@ -29,8 +29,8 @@ __kernel_gettimeofday: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */ tml %r4,0x0001 /* pending update ? loop */ jnz 1b - stck 24(%r15) /* Store TOD clock */ - lm %r0,%r1,24(%r15) + stcke 24(%r15) /* Store TOD clock */ + lm %r0,%r1,25(%r15) s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ sl %r1,__VDSO_XTIME_STAMP+4(%r5) brc 3,3f diff --git a/arch/s390/kernel/vdso64/clock_getres.S b/arch/s390/kernel/vdso64/clock_getres.S index 34deba7c7ed1d8b18036b0b07f9b7cd9d41e9259..c8513deb8c663f51fa4f4bc2b2299ea24f1aaefd 100644 --- a/arch/s390/kernel/vdso64/clock_getres.S +++ b/arch/s390/kernel/vdso64/clock_getres.S @@ -19,6 +19,12 @@ .type __kernel_clock_getres,@function __kernel_clock_getres: .cfi_startproc + larl %r1,4f + cghi %r2,__CLOCK_REALTIME_COARSE + je 0f + cghi %r2,__CLOCK_MONOTONIC_COARSE + je 0f + larl %r1,3f cghi %r2,__CLOCK_REALTIME je 0f cghi %r2,__CLOCK_MONOTONIC @@ -32,7 +38,6 @@ __kernel_clock_getres: jz 2f 0: ltgr %r3,%r3 jz 1f /* res == NULL */ - larl %r1,3f lg %r0,0(%r1) xc 0(8,%r3),0(%r3) /* set tp->tv_sec to zero */ stg %r0,8(%r3) /* store tp->tv_usec */ @@ -42,5 +47,6 @@ __kernel_clock_getres: svc 0 br %r14 3: .quad __CLOCK_REALTIME_RES +4: .quad __CLOCK_COARSE_RES .cfi_endproc .size __kernel_clock_getres,.-__kernel_clock_getres diff --git a/arch/s390/kernel/vdso64/clock_gettime.S b/arch/s390/kernel/vdso64/clock_gettime.S index 3f34e09db5f4d4d0e5f7a12b9e3acb89a72a6773..9d9761f8e11035761c3b1143c6486a9fc52193c6 100644 --- a/arch/s390/kernel/vdso64/clock_gettime.S +++ b/arch/s390/kernel/vdso64/clock_gettime.S @@ -20,12 +20,16 @@ __kernel_clock_gettime: .cfi_startproc larl %r5,_vdso_data + cghi %r2,__CLOCK_REALTIME_COARSE + je 4f cghi %r2,__CLOCK_REALTIME je 5f cghi %r2,__CLOCK_THREAD_CPUTIME_ID je 9f cghi %r2,-2 /* Per-thread CPUCLOCK with PID=0, VIRT=1 */ je 9f + cghi %r2,__CLOCK_MONOTONIC_COARSE + je 3f cghi %r2,__CLOCK_MONOTONIC jne 12f @@ -33,10 +37,10 @@ __kernel_clock_gettime: 0: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */ tmll %r4,0x0001 /* pending update ? loop */ jnz 0b - stck 48(%r15) /* Store TOD clock */ + stcke 48(%r15) /* Store TOD clock */ lgf %r2,__VDSO_TK_SHIFT(%r5) /* Timekeeper shift */ lg %r0,__VDSO_WTOM_SEC(%r5) - lg %r1,48(%r15) + lg %r1,49(%r15) sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ msgf %r1,__VDSO_TK_MULT(%r5) /* * tk->mult */ alg %r1,__VDSO_WTOM_NSEC(%r5) @@ -54,13 +58,33 @@ __kernel_clock_gettime: lghi %r2,0 br %r14 + /* CLOCK_MONOTONIC_COARSE */ +3: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */ + tmll %r4,0x0001 /* pending update ? loop */ + jnz 3b + lg %r0,__VDSO_WTOM_CRS_SEC(%r5) + lg %r1,__VDSO_WTOM_CRS_NSEC(%r5) + clg %r4,__VDSO_UPD_COUNT(%r5) /* check update counter */ + jne 3b + j 2b + + /* CLOCK_REALTIME_COARSE */ +4: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */ + tmll %r4,0x0001 /* pending update ? loop */ + jnz 4b + lg %r0,__VDSO_XTIME_CRS_SEC(%r5) + lg %r1,__VDSO_XTIME_CRS_NSEC(%r5) + clg %r4,__VDSO_UPD_COUNT(%r5) /* check update counter */ + jne 4b + j 7f + /* CLOCK_REALTIME */ 5: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */ tmll %r4,0x0001 /* pending update ? loop */ jnz 5b - stck 48(%r15) /* Store TOD clock */ + stcke 48(%r15) /* Store TOD clock */ lgf %r2,__VDSO_TK_SHIFT(%r5) /* Timekeeper shift */ - lg %r1,48(%r15) + lg %r1,49(%r15) sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ msgf %r1,__VDSO_TK_MULT(%r5) /* * tk->mult */ alg %r1,__VDSO_XTIME_NSEC(%r5) /* + tk->xtime_nsec */ diff --git a/arch/s390/kernel/vdso64/gettimeofday.S b/arch/s390/kernel/vdso64/gettimeofday.S index d0860d1d0cccfafe86320a07380ea14ddff1a284..7a344995a97fd21246de5554f09674449db8f2fd 100644 --- a/arch/s390/kernel/vdso64/gettimeofday.S +++ b/arch/s390/kernel/vdso64/gettimeofday.S @@ -28,8 +28,8 @@ __kernel_gettimeofday: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */ tmll %r4,0x0001 /* pending update ? loop */ jnz 0b - stck 48(%r15) /* Store TOD clock */ - lg %r1,48(%r15) + stcke 48(%r15) /* Store TOD clock */ + lg %r1,49(%r15) sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ msgf %r1,__VDSO_TK_MULT(%r5) /* * tk->mult */ alg %r1,__VDSO_XTIME_NSEC(%r5) /* + tk->xtime_nsec */ diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index 8c34363d6f1e88571074b01593b1efaa80c007d0..416f2a323ba5e3a3d59a806a921751749ccb7a46 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -6,27 +6,18 @@ */ #include -#include -#include #include #include #include #include #include -#include -#include -#include #include #include #include -#include -#include "entry.h" static void virt_timer_expire(void); -DEFINE_PER_CPU(struct s390_idle_data, s390_idle); - static LIST_HEAD(virt_timer_list); static DEFINE_SPINLOCK(virt_timer_lock); static atomic64_t virt_timer_current; @@ -152,49 +143,6 @@ void vtime_account_system(struct task_struct *tsk) __attribute__((alias("vtime_account_irq_enter"))); EXPORT_SYMBOL_GPL(vtime_account_system); -void __kprobes vtime_stop_cpu(void) -{ - struct s390_idle_data *idle = &__get_cpu_var(s390_idle); - unsigned long long idle_time; - unsigned long psw_mask; - - trace_hardirqs_on(); - - /* Wait for external, I/O or machine check interrupt. */ - psw_mask = PSW_KERNEL_BITS | PSW_MASK_WAIT | PSW_MASK_DAT | - PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK; - idle->nohz_delay = 0; - - /* Call the assembler magic in entry.S */ - psw_idle(idle, psw_mask); - - /* Account time spent with enabled wait psw loaded as idle time. */ - idle->sequence++; - smp_wmb(); - idle_time = idle->clock_idle_exit - idle->clock_idle_enter; - idle->clock_idle_enter = idle->clock_idle_exit = 0ULL; - idle->idle_time += idle_time; - idle->idle_count++; - account_idle_time(idle_time); - smp_wmb(); - idle->sequence++; -} - -cputime64_t s390_get_idle_time(int cpu) -{ - struct s390_idle_data *idle = &per_cpu(s390_idle, cpu); - unsigned long long now, idle_enter, idle_exit; - unsigned int sequence; - - do { - now = get_tod_clock(); - sequence = ACCESS_ONCE(idle->sequence); - idle_enter = ACCESS_ONCE(idle->clock_idle_enter); - idle_exit = ACCESS_ONCE(idle->clock_idle_exit); - } while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence)); - return idle_enter ? ((idle_exit ?: now) - idle_enter) : 0; -} - /* * Sorted add to a list. List is linear searched until first bigger * element is found. @@ -372,31 +320,8 @@ EXPORT_SYMBOL(del_virt_timer); /* * Start the virtual CPU timer on the current CPU. */ -void init_cpu_vtimer(void) +void vtime_init(void) { /* set initial cpu timer */ set_vtimer(VTIMER_MAX_SLICE); } - -static int s390_nohz_notify(struct notifier_block *self, unsigned long action, - void *hcpu) -{ - struct s390_idle_data *idle; - long cpu = (long) hcpu; - - idle = &per_cpu(s390_idle, cpu); - switch (action & ~CPU_TASKS_FROZEN) { - case CPU_DYING: - idle->nohz_delay = 0; - default: - break; - } - return NOTIFY_OK; -} - -void __init vtime_init(void) -{ - /* Enable cpu timer interrupts on the boot cpu. */ - init_cpu_vtimer(); - cpu_notifier(s390_nohz_notify, 0); -} diff --git a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile index c6d752e8bf28e2dbee2839915b46d10107eb047b..a01df233856f40bafef9424115f59026546be824 100644 --- a/arch/s390/lib/Makefile +++ b/arch/s390/lib/Makefile @@ -6,3 +6,5 @@ lib-y += delay.o string.o uaccess.o find.o obj-$(CONFIG_32BIT) += div64.o qrnnd.o ucmpdi2.o mem32.o obj-$(CONFIG_64BIT) += mem64.o lib-$(CONFIG_SMP) += spinlock.o +lib-$(CONFIG_KPROBES) += probes.o +lib-$(CONFIG_UPROBES) += probes.o diff --git a/arch/s390/lib/delay.c b/arch/s390/lib/delay.c index a9f3d0042d58ba849b8c624f23915d88d03d6f70..16dc42d83f9316c8524436457b2a92df59b2fbf0 100644 --- a/arch/s390/lib/delay.c +++ b/arch/s390/lib/delay.c @@ -43,7 +43,7 @@ static void __udelay_disabled(unsigned long long usecs) lockdep_off(); do { set_clock_comparator(end); - vtime_stop_cpu(); + enabled_wait(); } while (get_tod_clock_fast() < end); lockdep_on(); __ctl_load(cr0, 0, 0); @@ -62,7 +62,7 @@ static void __udelay_enabled(unsigned long long usecs) clock_saved = local_tick_disable(); set_clock_comparator(end); } - vtime_stop_cpu(); + enabled_wait(); if (clock_saved) local_tick_enable(clock_saved); } while (get_tod_clock_fast() < end); diff --git a/arch/s390/lib/probes.c b/arch/s390/lib/probes.c new file mode 100644 index 0000000000000000000000000000000000000000..c5d64a0997193f77d0cd56ce2e7ac2c62c0b3f28 --- /dev/null +++ b/arch/s390/lib/probes.c @@ -0,0 +1,159 @@ +/* + * Common helper functions for kprobes and uprobes + * + * Copyright IBM Corp. 2014 + */ + +#include +#include + +int probe_is_prohibited_opcode(u16 *insn) +{ + if (!is_known_insn((unsigned char *)insn)) + return -EINVAL; + switch (insn[0] >> 8) { + case 0x0c: /* bassm */ + case 0x0b: /* bsm */ + case 0x83: /* diag */ + case 0x44: /* ex */ + case 0xac: /* stnsm */ + case 0xad: /* stosm */ + return -EINVAL; + case 0xc6: + switch (insn[0] & 0x0f) { + case 0x00: /* exrl */ + return -EINVAL; + } + } + switch (insn[0]) { + case 0x0101: /* pr */ + case 0xb25a: /* bsa */ + case 0xb240: /* bakr */ + case 0xb258: /* bsg */ + case 0xb218: /* pc */ + case 0xb228: /* pt */ + case 0xb98d: /* epsw */ + case 0xe560: /* tbegin */ + case 0xe561: /* tbeginc */ + case 0xb2f8: /* tend */ + return -EINVAL; + } + return 0; +} + +int probe_get_fixup_type(u16 *insn) +{ + /* default fixup method */ + int fixup = FIXUP_PSW_NORMAL; + + switch (insn[0] >> 8) { + case 0x05: /* balr */ + case 0x0d: /* basr */ + fixup = FIXUP_RETURN_REGISTER; + /* if r2 = 0, no branch will be taken */ + if ((insn[0] & 0x0f) == 0) + fixup |= FIXUP_BRANCH_NOT_TAKEN; + break; + case 0x06: /* bctr */ + case 0x07: /* bcr */ + fixup = FIXUP_BRANCH_NOT_TAKEN; + break; + case 0x45: /* bal */ + case 0x4d: /* bas */ + fixup = FIXUP_RETURN_REGISTER; + break; + case 0x47: /* bc */ + case 0x46: /* bct */ + case 0x86: /* bxh */ + case 0x87: /* bxle */ + fixup = FIXUP_BRANCH_NOT_TAKEN; + break; + case 0x82: /* lpsw */ + fixup = FIXUP_NOT_REQUIRED; + break; + case 0xb2: /* lpswe */ + if ((insn[0] & 0xff) == 0xb2) + fixup = FIXUP_NOT_REQUIRED; + break; + case 0xa7: /* bras */ + if ((insn[0] & 0x0f) == 0x05) + fixup |= FIXUP_RETURN_REGISTER; + break; + case 0xc0: + if ((insn[0] & 0x0f) == 0x05) /* brasl */ + fixup |= FIXUP_RETURN_REGISTER; + break; + case 0xeb: + switch (insn[2] & 0xff) { + case 0x44: /* bxhg */ + case 0x45: /* bxleg */ + fixup = FIXUP_BRANCH_NOT_TAKEN; + break; + } + break; + case 0xe3: /* bctg */ + if ((insn[2] & 0xff) == 0x46) + fixup = FIXUP_BRANCH_NOT_TAKEN; + break; + case 0xec: + switch (insn[2] & 0xff) { + case 0xe5: /* clgrb */ + case 0xe6: /* cgrb */ + case 0xf6: /* crb */ + case 0xf7: /* clrb */ + case 0xfc: /* cgib */ + case 0xfd: /* cglib */ + case 0xfe: /* cib */ + case 0xff: /* clib */ + fixup = FIXUP_BRANCH_NOT_TAKEN; + break; + } + break; + } + return fixup; +} + +int probe_is_insn_relative_long(u16 *insn) +{ + /* Check if we have a RIL-b or RIL-c format instruction which + * we need to modify in order to avoid instruction emulation. */ + switch (insn[0] >> 8) { + case 0xc0: + if ((insn[0] & 0x0f) == 0x00) /* larl */ + return true; + break; + case 0xc4: + switch (insn[0] & 0x0f) { + case 0x02: /* llhrl */ + case 0x04: /* lghrl */ + case 0x05: /* lhrl */ + case 0x06: /* llghrl */ + case 0x07: /* sthrl */ + case 0x08: /* lgrl */ + case 0x0b: /* stgrl */ + case 0x0c: /* lgfrl */ + case 0x0d: /* lrl */ + case 0x0e: /* llgfrl */ + case 0x0f: /* strl */ + return true; + } + break; + case 0xc6: + switch (insn[0] & 0x0f) { + case 0x02: /* pfdrl */ + case 0x04: /* cghrl */ + case 0x05: /* chrl */ + case 0x06: /* clghrl */ + case 0x07: /* clhrl */ + case 0x08: /* cgrl */ + case 0x0a: /* clgrl */ + case 0x0c: /* cgfrl */ + case 0x0d: /* crl */ + case 0x0e: /* clgfrl */ + case 0x0f: /* clrl */ + return true; + } + break; + } + return false; +} diff --git a/arch/s390/lib/spinlock.c b/arch/s390/lib/spinlock.c index 5b0e445bc3f39930317bafc745c76c674874bf0e..034a35a3e9c11a0a5a9b7f2d73ba16cf9adbd314 100644 --- a/arch/s390/lib/spinlock.c +++ b/arch/s390/lib/spinlock.c @@ -98,17 +98,6 @@ void arch_spin_lock_wait_flags(arch_spinlock_t *lp, unsigned long flags) } EXPORT_SYMBOL(arch_spin_lock_wait_flags); -void arch_spin_relax(arch_spinlock_t *lp) -{ - unsigned int cpu = lp->lock; - if (cpu != 0) { - if (MACHINE_IS_VM || MACHINE_IS_KVM || - !smp_vcpu_scheduled(~cpu)) - smp_yield_cpu(~cpu); - } -} -EXPORT_SYMBOL(arch_spin_relax); - int arch_spin_trylock_retry(arch_spinlock_t *lp) { int count; @@ -122,15 +111,21 @@ EXPORT_SYMBOL(arch_spin_trylock_retry); void _raw_read_lock_wait(arch_rwlock_t *rw) { - unsigned int old; + unsigned int owner, old; int count = spin_retry; +#ifdef CONFIG_HAVE_MARCH_Z196_FEATURES + __RAW_LOCK(&rw->lock, -1, __RAW_OP_ADD); +#endif + owner = 0; while (1) { if (count-- <= 0) { - smp_yield(); + if (owner && !smp_vcpu_scheduled(~owner)) + smp_yield_cpu(~owner); count = spin_retry; } old = ACCESS_ONCE(rw->lock); + owner = ACCESS_ONCE(rw->owner); if ((int) old < 0) continue; if (_raw_compare_and_swap(&rw->lock, old, old + 1)) @@ -139,28 +134,6 @@ void _raw_read_lock_wait(arch_rwlock_t *rw) } EXPORT_SYMBOL(_raw_read_lock_wait); -void _raw_read_lock_wait_flags(arch_rwlock_t *rw, unsigned long flags) -{ - unsigned int old; - int count = spin_retry; - - local_irq_restore(flags); - while (1) { - if (count-- <= 0) { - smp_yield(); - count = spin_retry; - } - old = ACCESS_ONCE(rw->lock); - if ((int) old < 0) - continue; - local_irq_disable(); - if (_raw_compare_and_swap(&rw->lock, old, old + 1)) - return; - local_irq_restore(flags); - } -} -EXPORT_SYMBOL(_raw_read_lock_wait_flags); - int _raw_read_trylock_retry(arch_rwlock_t *rw) { unsigned int old; @@ -177,46 +150,62 @@ int _raw_read_trylock_retry(arch_rwlock_t *rw) } EXPORT_SYMBOL(_raw_read_trylock_retry); -void _raw_write_lock_wait(arch_rwlock_t *rw) +#ifdef CONFIG_HAVE_MARCH_Z196_FEATURES + +void _raw_write_lock_wait(arch_rwlock_t *rw, unsigned int prev) { - unsigned int old; + unsigned int owner, old; int count = spin_retry; + owner = 0; while (1) { if (count-- <= 0) { - smp_yield(); + if (owner && !smp_vcpu_scheduled(~owner)) + smp_yield_cpu(~owner); count = spin_retry; } old = ACCESS_ONCE(rw->lock); - if (old) - continue; - if (_raw_compare_and_swap(&rw->lock, 0, 0x80000000)) - return; + owner = ACCESS_ONCE(rw->owner); + smp_rmb(); + if ((int) old >= 0) { + prev = __RAW_LOCK(&rw->lock, 0x80000000, __RAW_OP_OR); + old = prev; + } + if ((old & 0x7fffffff) == 0 && (int) prev >= 0) + break; } } EXPORT_SYMBOL(_raw_write_lock_wait); -void _raw_write_lock_wait_flags(arch_rwlock_t *rw, unsigned long flags) +#else /* CONFIG_HAVE_MARCH_Z196_FEATURES */ + +void _raw_write_lock_wait(arch_rwlock_t *rw) { - unsigned int old; + unsigned int owner, old, prev; int count = spin_retry; - local_irq_restore(flags); + prev = 0x80000000; + owner = 0; while (1) { if (count-- <= 0) { - smp_yield(); + if (owner && !smp_vcpu_scheduled(~owner)) + smp_yield_cpu(~owner); count = spin_retry; } old = ACCESS_ONCE(rw->lock); - if (old) - continue; - local_irq_disable(); - if (_raw_compare_and_swap(&rw->lock, 0, 0x80000000)) - return; - local_irq_restore(flags); + owner = ACCESS_ONCE(rw->owner); + if ((int) old >= 0 && + _raw_compare_and_swap(&rw->lock, old, old | 0x80000000)) + prev = old; + else + smp_rmb(); + if ((old & 0x7fffffff) == 0 && (int) prev >= 0) + break; } } -EXPORT_SYMBOL(_raw_write_lock_wait_flags); +EXPORT_SYMBOL(_raw_write_lock_wait); + +#endif /* CONFIG_HAVE_MARCH_Z196_FEATURES */ int _raw_write_trylock_retry(arch_rwlock_t *rw) { @@ -233,3 +222,13 @@ int _raw_write_trylock_retry(arch_rwlock_t *rw) return 0; } EXPORT_SYMBOL(_raw_write_trylock_retry); + +void arch_lock_relax(unsigned int cpu) +{ + if (!cpu) + return; + if (MACHINE_IS_LPAR && smp_vcpu_scheduled(~cpu)) + return; + smp_yield_cpu(~cpu); +} +EXPORT_SYMBOL(arch_lock_relax); diff --git a/arch/s390/mm/dump_pagetables.c b/arch/s390/mm/dump_pagetables.c index 46d517c3c76366c7459b7f539888a29064a43d78..d46cadeda204d5a51308ec7a7a4b1d2e100de0ee 100644 --- a/arch/s390/mm/dump_pagetables.c +++ b/arch/s390/mm/dump_pagetables.c @@ -54,7 +54,6 @@ static void print_prot(struct seq_file *m, unsigned int pr, int level) return; } seq_printf(m, "%s", pr & _PAGE_PROTECT ? "RO " : "RW "); - seq_printf(m, "%s", pr & _PAGE_CO ? "CO " : " "); seq_putc(m, '\n'); } @@ -129,7 +128,7 @@ static void walk_pte_level(struct seq_file *m, struct pg_state *st, } #ifdef CONFIG_64BIT -#define _PMD_PROT_MASK (_SEGMENT_ENTRY_PROTECT | _SEGMENT_ENTRY_CO) +#define _PMD_PROT_MASK _SEGMENT_ENTRY_PROTECT #else #define _PMD_PROT_MASK 0 #endif @@ -157,7 +156,7 @@ static void walk_pmd_level(struct seq_file *m, struct pg_state *st, } #ifdef CONFIG_64BIT -#define _PUD_PROT_MASK (_REGION3_ENTRY_RO | _REGION3_ENTRY_CO) +#define _PUD_PROT_MASK _REGION3_ENTRY_RO #else #define _PUD_PROT_MASK 0 #endif diff --git a/arch/s390/mm/hugetlbpage.c b/arch/s390/mm/hugetlbpage.c index 389bc17934b7887625598963284b12e8900ce4fe..3c80d2e38f03c45c014eb3a46551cb0e14a920de 100644 --- a/arch/s390/mm/hugetlbpage.c +++ b/arch/s390/mm/hugetlbpage.c @@ -88,7 +88,7 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, pmd_val(pmd) &= ~_SEGMENT_ENTRY_ORIGIN; pmd_val(pmd) |= pte_page(pte)[1].index; } else - pmd_val(pmd) |= _SEGMENT_ENTRY_LARGE | _SEGMENT_ENTRY_CO; + pmd_val(pmd) |= _SEGMENT_ENTRY_LARGE; *(pmd_t *) ptep = pmd; } diff --git a/arch/s390/mm/pageattr.c b/arch/s390/mm/pageattr.c index 8400f494623f4591a5de3e5ea6442a08fe767ed4..3fef3b299665797bbd486baec5239d5f88705428 100644 --- a/arch/s390/mm/pageattr.c +++ b/arch/s390/mm/pageattr.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -103,27 +104,50 @@ int set_memory_x(unsigned long addr, int numpages) } #ifdef CONFIG_DEBUG_PAGEALLOC + +static void ipte_range(pte_t *pte, unsigned long address, int nr) +{ + int i; + + if (test_facility(13) && IS_ENABLED(CONFIG_64BIT)) { + __ptep_ipte_range(address, nr - 1, pte); + return; + } + for (i = 0; i < nr; i++) { + __ptep_ipte(address, pte); + address += PAGE_SIZE; + pte++; + } +} + void kernel_map_pages(struct page *page, int numpages, int enable) { unsigned long address; + int nr, i, j; pgd_t *pgd; pud_t *pud; pmd_t *pmd; pte_t *pte; - int i; - for (i = 0; i < numpages; i++) { + for (i = 0; i < numpages;) { address = page_to_phys(page + i); pgd = pgd_offset_k(address); pud = pud_offset(pgd, address); pmd = pmd_offset(pud, address); pte = pte_offset_kernel(pmd, address); - if (!enable) { - __ptep_ipte(address, pte); - pte_val(*pte) = _PAGE_INVALID; - continue; + nr = (unsigned long)pte >> ilog2(sizeof(long)); + nr = PTRS_PER_PTE - (nr & (PTRS_PER_PTE - 1)); + nr = min(numpages - i, nr); + if (enable) { + for (j = 0; j < nr; j++) { + pte_val(*pte) = __pa(address); + address += PAGE_SIZE; + pte++; + } + } else { + ipte_range(pte, address, nr); } - pte_val(*pte) = __pa(address); + i += nr; } } diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index fdbd7888cb076f87a9b6f9d94dcae434ccac77e5..b1593c2f751a89aa71675f1375d412d6736a6673 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -236,8 +236,7 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node) if (!new_page) goto out; pmd_val(*pm_dir) = __pa(new_page) | - _SEGMENT_ENTRY | _SEGMENT_ENTRY_LARGE | - _SEGMENT_ENTRY_CO; + _SEGMENT_ENTRY | _SEGMENT_ENTRY_LARGE; address = (address + PMD_SIZE) & PMD_MASK; continue; } @@ -253,9 +252,9 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node) pt_dir = pte_offset_kernel(pm_dir, address); if (pte_none(*pt_dir)) { - unsigned long new_page; + void *new_page; - new_page =__pa(vmem_alloc_pages(0)); + new_page = vmemmap_alloc_block(PAGE_SIZE, node); if (!new_page) goto out; pte_val(*pt_dir) = @@ -263,7 +262,6 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node) } address += PAGE_SIZE; } - memset((void *)start, 0, end - start); ret = 0; out: return ret; diff --git a/arch/s390/oprofile/hwsampler.c b/arch/s390/oprofile/hwsampler.c index e53c6f268807aa5c15ec6dc8ff87a67bdc633d88..ff9b4eb3458908ccd70da612c346659f734e7641 100644 --- a/arch/s390/oprofile/hwsampler.c +++ b/arch/s390/oprofile/hwsampler.c @@ -178,7 +178,7 @@ static int smp_ctl_qsi(int cpu) static void hws_ext_handler(struct ext_code ext_code, unsigned int param32, unsigned long param64) { - struct hws_cpu_buffer *cb = &__get_cpu_var(sampler_cpu_buffer); + struct hws_cpu_buffer *cb = this_cpu_ptr(&sampler_cpu_buffer); if (!(param32 & CPU_MF_INT_SF_MASK)) return; diff --git a/arch/sh/include/asm/syscall_32.h b/arch/sh/include/asm/syscall_32.h index 7d80df4f09cb6349117329cd2ce8cd50ccfa64d8..95be3b0ce0acd575e3d006a5ec31329b16dbcbf1 100644 --- a/arch/sh/include/asm/syscall_32.h +++ b/arch/sh/include/asm/syscall_32.h @@ -1,6 +1,7 @@ #ifndef __ASM_SH_SYSCALL_32_H #define __ASM_SH_SYSCALL_32_H +#include #include #include #include @@ -93,4 +94,13 @@ static inline void syscall_set_arguments(struct task_struct *task, } } +static inline int syscall_get_arch(void) +{ + int arch = AUDIT_ARCH_SH; + +#ifdef CONFIG_CPU_LITTLE_ENDIAN + arch |= __AUDIT_ARCH_LE; +#endif + return arch; +} #endif /* __ASM_SH_SYSCALL_32_H */ diff --git a/arch/sh/include/asm/syscall_64.h b/arch/sh/include/asm/syscall_64.h index c3561ca72bee9fb284dbe52b823da3ca6feadbc0..c6a797b90b8052f1dfc879a41f722c2fabac8c63 100644 --- a/arch/sh/include/asm/syscall_64.h +++ b/arch/sh/include/asm/syscall_64.h @@ -1,6 +1,7 @@ #ifndef __ASM_SH_SYSCALL_64_H #define __ASM_SH_SYSCALL_64_H +#include #include #include #include @@ -61,4 +62,17 @@ static inline void syscall_set_arguments(struct task_struct *task, memcpy(®s->regs[2 + i], args, n * sizeof(args[0])); } +static inline int syscall_get_arch(void) +{ + int arch = AUDIT_ARCH_SH; + +#ifdef CONFIG_64BIT + arch |= __AUDIT_ARCH_64BIT; +#endif +#ifdef CONFIG_CPU_LITTLE_ENDIAN + arch |= __AUDIT_ARCH_LE; +#endif + + return arch; +} #endif /* __ASM_SH_SYSCALL_64_H */ diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index 668c81631c083d352dd50b3afc63193708d44eac..c1a6b89bfe70d588b1aabf5d2814196c505018a8 100644 --- a/arch/sh/kernel/ptrace_32.c +++ b/arch/sh/kernel/ptrace_32.c @@ -484,17 +484,6 @@ long arch_ptrace(struct task_struct *child, long request, return ret; } -static inline int audit_arch(void) -{ - int arch = EM_SH; - -#ifdef CONFIG_CPU_LITTLE_ENDIAN - arch |= __AUDIT_ARCH_LE; -#endif - - return arch; -} - asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) { long ret = 0; @@ -513,8 +502,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_enter(regs, regs->regs[0]); - audit_syscall_entry(audit_arch(), regs->regs[3], - regs->regs[4], regs->regs[5], + audit_syscall_entry(regs->regs[3], regs->regs[4], regs->regs[5], regs->regs[6], regs->regs[7]); return ret ?: regs->regs[0]; diff --git a/arch/sh/kernel/ptrace_64.c b/arch/sh/kernel/ptrace_64.c index af90339dadcd7aa5f02dd0f4a59c10a61ccf98d6..5cea973a65b25674a34fefb411483d474a723351 100644 --- a/arch/sh/kernel/ptrace_64.c +++ b/arch/sh/kernel/ptrace_64.c @@ -504,20 +504,6 @@ asmlinkage int sh64_ptrace(long request, long pid, return sys_ptrace(request, pid, addr, data); } -static inline int audit_arch(void) -{ - int arch = EM_SH; - -#ifdef CONFIG_64BIT - arch |= __AUDIT_ARCH_64BIT; -#endif -#ifdef CONFIG_CPU_LITTLE_ENDIAN - arch |= __AUDIT_ARCH_LE; -#endif - - return arch; -} - asmlinkage long long do_syscall_trace_enter(struct pt_regs *regs) { long long ret = 0; @@ -536,8 +522,7 @@ asmlinkage long long do_syscall_trace_enter(struct pt_regs *regs) if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_enter(regs, regs->regs[9]); - audit_syscall_entry(audit_arch(), regs->regs[1], - regs->regs[2], regs->regs[3], + audit_syscall_entry(regs->regs[1], regs->regs[2], regs->regs[3], regs->regs[4], regs->regs[5]); return ret ?: regs->regs[9]; diff --git a/arch/sparc/include/asm/cpudata_32.h b/arch/sparc/include/asm/cpudata_32.h index 0300d94c25b35722f30e72d36d2c558ebf3d8bc3..05f366379f5336ce239205cb956c49ada99ec18e 100644 --- a/arch/sparc/include/asm/cpudata_32.h +++ b/arch/sparc/include/asm/cpudata_32.h @@ -26,6 +26,6 @@ typedef struct { DECLARE_PER_CPU(cpuinfo_sparc, __cpu_data); #define cpu_data(__cpu) per_cpu(__cpu_data, (__cpu)) -#define local_cpu_data() __get_cpu_var(__cpu_data) +#define local_cpu_data() (*this_cpu_ptr(&__cpu_data)) #endif /* _SPARC_CPUDATA_H */ diff --git a/arch/sparc/include/asm/cpudata_64.h b/arch/sparc/include/asm/cpudata_64.h index 0e594076912c07f5d5a3114ec87ec14429436288..a6e424d185d063bdddd43719e6a994fad6902c17 100644 --- a/arch/sparc/include/asm/cpudata_64.h +++ b/arch/sparc/include/asm/cpudata_64.h @@ -30,7 +30,7 @@ typedef struct { DECLARE_PER_CPU(cpuinfo_sparc, __cpu_data); #define cpu_data(__cpu) per_cpu(__cpu_data, (__cpu)) -#define local_cpu_data() __get_cpu_var(__cpu_data) +#define local_cpu_data() (*this_cpu_ptr(&__cpu_data)) #endif /* !(__ASSEMBLY__) */ diff --git a/arch/sparc/include/asm/syscall.h b/arch/sparc/include/asm/syscall.h index 025a02ad2e31c9a87c15350241234b8943e04ae6..49f71fd5b56ee83566895b0674dca3bd6c7057e5 100644 --- a/arch/sparc/include/asm/syscall.h +++ b/arch/sparc/include/asm/syscall.h @@ -1,9 +1,11 @@ #ifndef __ASM_SPARC_SYSCALL_H #define __ASM_SPARC_SYSCALL_H +#include #include #include #include +#include /* * The syscall table always contains 32 bit pointers since we know that the @@ -124,4 +126,9 @@ static inline void syscall_set_arguments(struct task_struct *task, regs->u_regs[UREG_I0 + i + j] = args[j]; } +static inline int syscall_get_arch(void) +{ + return is_32bit_task() ? AUDIT_ARCH_SPARC : AUDIT_ARCH_SPARC64; +} + #endif /* __ASM_SPARC_SYSCALL_H */ diff --git a/arch/sparc/include/asm/thread_info_32.h b/arch/sparc/include/asm/thread_info_32.h index 96efa7adc22330d2684e67ab3c83dafa99e89e8a..025c98446b1ee1bce743d41ecdf4494eb62e7d50 100644 --- a/arch/sparc/include/asm/thread_info_32.h +++ b/arch/sparc/include/asm/thread_info_32.h @@ -130,6 +130,8 @@ register struct thread_info *current_thread_info_reg asm("g6"); #define _TIF_DO_NOTIFY_RESUME_MASK (_TIF_NOTIFY_RESUME | \ _TIF_SIGPENDING) +#define is_32bit_task() (1) + #endif /* __KERNEL__ */ #endif /* _ASM_THREAD_INFO_H */ diff --git a/arch/sparc/include/asm/thread_info_64.h b/arch/sparc/include/asm/thread_info_64.h index f85dc8512ab3f6fdea1f1430aa8141bdccec2d44..798f0279a4b56a3ccd23b29c7b527bbd329b3061 100644 --- a/arch/sparc/include/asm/thread_info_64.h +++ b/arch/sparc/include/asm/thread_info_64.h @@ -63,7 +63,8 @@ struct thread_info { struct pt_regs *kern_una_regs; unsigned int kern_una_insn; - unsigned long fpregs[0] __attribute__ ((aligned(64))); + unsigned long fpregs[(7 * 256) / sizeof(unsigned long)] + __attribute__ ((aligned(64))); }; #endif /* !(__ASSEMBLY__) */ @@ -220,6 +221,8 @@ register struct thread_info *current_thread_info_reg asm("g6"); _TIF_NEED_RESCHED) #define _TIF_DO_NOTIFY_RESUME_MASK (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING) +#define is_32bit_task() (test_thread_flag(TIF_32BIT)) + /* * Thread-synchronous status. * diff --git a/arch/sparc/include/asm/visasm.h b/arch/sparc/include/asm/visasm.h index b2667375928306d1442c475604fb0d919fd4bf09..1f0aa2024e94be341efc079f58145a2e70909052 100644 --- a/arch/sparc/include/asm/visasm.h +++ b/arch/sparc/include/asm/visasm.h @@ -39,6 +39,14 @@ 297: wr %o5, FPRS_FEF, %fprs; \ 298: +#define VISEntryHalfFast(fail_label) \ + rd %fprs, %o5; \ + andcc %o5, FPRS_FEF, %g0; \ + be,pt %icc, 297f; \ + nop; \ + ba,a,pt %xcc, fail_label; \ +297: wr %o5, FPRS_FEF, %fprs; + #define VISExitHalf \ wr %o5, 0, %fprs; diff --git a/arch/sparc/kernel/dtlb_prot.S b/arch/sparc/kernel/dtlb_prot.S index b2c2c5be281c436f25b8cb66353546d9fcd8f82c..d668ca149e647ac832f88b3602134768d712ef80 100644 --- a/arch/sparc/kernel/dtlb_prot.S +++ b/arch/sparc/kernel/dtlb_prot.S @@ -24,11 +24,11 @@ mov TLB_TAG_ACCESS, %g4 ! For reload of vaddr /* PROT ** ICACHE line 2: More real fault processing */ + ldxa [%g4] ASI_DMMU, %g5 ! Put tagaccess in %g5 bgu,pn %xcc, winfix_trampoline ! Yes, perform winfixup - ldxa [%g4] ASI_DMMU, %g5 ! Put tagaccess in %g5 - ba,pt %xcc, sparc64_realfault_common ! Nope, normal fault mov FAULT_CODE_DTLB | FAULT_CODE_WRITE, %g4 - nop + ba,pt %xcc, sparc64_realfault_common ! Nope, normal fault + nop nop nop nop diff --git a/arch/sparc/kernel/kprobes.c b/arch/sparc/kernel/kprobes.c index 98d71284341332d5c5bc0ecfeaa4acb17875d139..cd83be527586ee0be802266d8bc5f35c1b1b1121 100644 --- a/arch/sparc/kernel/kprobes.c +++ b/arch/sparc/kernel/kprobes.c @@ -83,7 +83,7 @@ static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) { - __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp; + __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp); kcb->kprobe_status = kcb->prev_kprobe.status; kcb->kprobe_orig_tnpc = kcb->prev_kprobe.orig_tnpc; kcb->kprobe_orig_tstate_pil = kcb->prev_kprobe.orig_tstate_pil; @@ -92,7 +92,7 @@ static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb) { - __get_cpu_var(current_kprobe) = p; + __this_cpu_write(current_kprobe, p); kcb->kprobe_orig_tnpc = regs->tnpc; kcb->kprobe_orig_tstate_pil = (regs->tstate & TSTATE_PIL); } @@ -155,7 +155,7 @@ static int __kprobes kprobe_handler(struct pt_regs *regs) ret = 1; goto no_kprobe; } - p = __get_cpu_var(current_kprobe); + p = __this_cpu_read(current_kprobe); if (p->break_handler && p->break_handler(p, regs)) goto ss_probe; } diff --git a/arch/sparc/kernel/leon_smp.c b/arch/sparc/kernel/leon_smp.c index 018ef11f57df7060a1bd83d7db6a35a82bfc7391..ea2bad306f9356d8e2803a61c09944360160edac 100644 --- a/arch/sparc/kernel/leon_smp.c +++ b/arch/sparc/kernel/leon_smp.c @@ -343,7 +343,7 @@ static void leon_ipi_resched(int cpu) void leonsmp_ipi_interrupt(void) { - struct leon_ipi_work *work = &__get_cpu_var(leon_ipi_work); + struct leon_ipi_work *work = this_cpu_ptr(&leon_ipi_work); if (work->single) { work->single = 0; diff --git a/arch/sparc/kernel/nmi.c b/arch/sparc/kernel/nmi.c index 5b1151dcba13b919cefde000a55949acb9a3ae6b..a9973bb4a1b228ae982bfd9a97fe9b7ab7aea6f4 100644 --- a/arch/sparc/kernel/nmi.c +++ b/arch/sparc/kernel/nmi.c @@ -100,20 +100,20 @@ notrace __kprobes void perfctr_irq(int irq, struct pt_regs *regs) pcr_ops->write_pcr(0, pcr_ops->pcr_nmi_disable); sum = local_cpu_data().irq0_irqs; - if (__get_cpu_var(nmi_touch)) { - __get_cpu_var(nmi_touch) = 0; + if (__this_cpu_read(nmi_touch)) { + __this_cpu_write(nmi_touch, 0); touched = 1; } - if (!touched && __get_cpu_var(last_irq_sum) == sum) { + if (!touched && __this_cpu_read(last_irq_sum) == sum) { __this_cpu_inc(alert_counter); if (__this_cpu_read(alert_counter) == 30 * nmi_hz) die_nmi("BUG: NMI Watchdog detected LOCKUP", regs, panic_on_timeout); } else { - __get_cpu_var(last_irq_sum) = sum; + __this_cpu_write(last_irq_sum, sum); __this_cpu_write(alert_counter, 0); } - if (__get_cpu_var(wd_enabled)) { + if (__this_cpu_read(wd_enabled)) { pcr_ops->write_pic(0, pcr_ops->nmi_picl_value(nmi_hz)); pcr_ops->write_pcr(0, pcr_ops->pcr_nmi_enable); } @@ -154,7 +154,7 @@ static void report_broken_nmi(int cpu, int *prev_nmi_count) void stop_nmi_watchdog(void *unused) { pcr_ops->write_pcr(0, pcr_ops->pcr_nmi_disable); - __get_cpu_var(wd_enabled) = 0; + __this_cpu_write(wd_enabled, 0); atomic_dec(&nmi_active); } @@ -207,7 +207,7 @@ static int __init check_nmi_watchdog(void) void start_nmi_watchdog(void *unused) { - __get_cpu_var(wd_enabled) = 1; + __this_cpu_write(wd_enabled, 1); atomic_inc(&nmi_active); pcr_ops->write_pcr(0, pcr_ops->pcr_nmi_disable); @@ -218,7 +218,7 @@ void start_nmi_watchdog(void *unused) static void nmi_adjust_hz_one(void *unused) { - if (!__get_cpu_var(wd_enabled)) + if (!__this_cpu_read(wd_enabled)) return; pcr_ops->write_pcr(0, pcr_ops->pcr_nmi_disable); diff --git a/arch/sparc/kernel/pci_sun4v.c b/arch/sparc/kernel/pci_sun4v.c index d07f6b29aed88510f1625d90655a4fbd10159b4c..49d33b178793c59ca04cd55a60ba5161f9f5e508 100644 --- a/arch/sparc/kernel/pci_sun4v.c +++ b/arch/sparc/kernel/pci_sun4v.c @@ -48,7 +48,7 @@ static int iommu_batch_initialized; /* Interrupts must be disabled. */ static inline void iommu_batch_start(struct device *dev, unsigned long prot, unsigned long entry) { - struct iommu_batch *p = &__get_cpu_var(iommu_batch); + struct iommu_batch *p = this_cpu_ptr(&iommu_batch); p->dev = dev; p->prot = prot; @@ -94,7 +94,7 @@ static long iommu_batch_flush(struct iommu_batch *p) static inline void iommu_batch_new_entry(unsigned long entry) { - struct iommu_batch *p = &__get_cpu_var(iommu_batch); + struct iommu_batch *p = this_cpu_ptr(&iommu_batch); if (p->entry + p->npages == entry) return; @@ -106,7 +106,7 @@ static inline void iommu_batch_new_entry(unsigned long entry) /* Interrupts must be disabled. */ static inline long iommu_batch_add(u64 phys_page) { - struct iommu_batch *p = &__get_cpu_var(iommu_batch); + struct iommu_batch *p = this_cpu_ptr(&iommu_batch); BUG_ON(p->npages >= PGLIST_NENTS); @@ -120,7 +120,7 @@ static inline long iommu_batch_add(u64 phys_page) /* Interrupts must be disabled. */ static inline long iommu_batch_end(void) { - struct iommu_batch *p = &__get_cpu_var(iommu_batch); + struct iommu_batch *p = this_cpu_ptr(&iommu_batch); BUG_ON(p->npages >= PGLIST_NENTS); diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index c9759ad3f34af8230104309eec4168bae2980708..46a5e4508752814cb0ec21b820673cd445b19ada 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c @@ -1013,7 +1013,7 @@ static void update_pcrs_for_enable(struct cpu_hw_events *cpuc) static void sparc_pmu_enable(struct pmu *pmu) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int i; if (cpuc->enabled) @@ -1031,7 +1031,7 @@ static void sparc_pmu_enable(struct pmu *pmu) static void sparc_pmu_disable(struct pmu *pmu) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int i; if (!cpuc->enabled) @@ -1065,7 +1065,7 @@ static int active_event_index(struct cpu_hw_events *cpuc, static void sparc_pmu_start(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int idx = active_event_index(cpuc, event); if (flags & PERF_EF_RELOAD) { @@ -1080,7 +1080,7 @@ static void sparc_pmu_start(struct perf_event *event, int flags) static void sparc_pmu_stop(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int idx = active_event_index(cpuc, event); if (!(event->hw.state & PERF_HES_STOPPED)) { @@ -1096,7 +1096,7 @@ static void sparc_pmu_stop(struct perf_event *event, int flags) static void sparc_pmu_del(struct perf_event *event, int _flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); unsigned long flags; int i; @@ -1133,7 +1133,7 @@ static void sparc_pmu_del(struct perf_event *event, int _flags) static void sparc_pmu_read(struct perf_event *event) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int idx = active_event_index(cpuc, event); struct hw_perf_event *hwc = &event->hw; @@ -1145,7 +1145,7 @@ static DEFINE_MUTEX(pmc_grab_mutex); static void perf_stop_nmi_watchdog(void *unused) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int i; stop_nmi_watchdog(NULL); @@ -1356,7 +1356,7 @@ static int collect_events(struct perf_event *group, int max_count, static int sparc_pmu_add(struct perf_event *event, int ef_flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int n0, ret = -EAGAIN; unsigned long flags; @@ -1498,7 +1498,7 @@ static int sparc_pmu_event_init(struct perf_event *event) */ static void sparc_pmu_start_txn(struct pmu *pmu) { - struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events); perf_pmu_disable(pmu); cpuhw->group_flag |= PERF_EVENT_TXN; @@ -1511,7 +1511,7 @@ static void sparc_pmu_start_txn(struct pmu *pmu) */ static void sparc_pmu_cancel_txn(struct pmu *pmu) { - struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events); cpuhw->group_flag &= ~PERF_EVENT_TXN; perf_pmu_enable(pmu); @@ -1524,13 +1524,13 @@ static void sparc_pmu_cancel_txn(struct pmu *pmu) */ static int sparc_pmu_commit_txn(struct pmu *pmu) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int n; if (!sparc_pmu) return -EINVAL; - cpuc = &__get_cpu_var(cpu_hw_events); + cpuc = this_cpu_ptr(&cpu_hw_events); n = cpuc->n_events; if (check_excludes(cpuc->event, 0, n)) return -EINVAL; @@ -1601,7 +1601,7 @@ static int __kprobes perf_event_nmi_handler(struct notifier_block *self, regs = args->regs; - cpuc = &__get_cpu_var(cpu_hw_events); + cpuc = this_cpu_ptr(&cpu_hw_events); /* If the PMU has the TOE IRQ enable bits, we need to do a * dummy write to the %pcr to clear the overflow bits and thus diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c index c13c9f25d83a0b11dc1d415bd556c1cd25d6f41b..9ddc4928a089b599568331792097c2bc35ea0be8 100644 --- a/arch/sparc/kernel/ptrace_64.c +++ b/arch/sparc/kernel/ptrace_64.c @@ -1076,13 +1076,8 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs) if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_enter(regs, regs->u_regs[UREG_G1]); - audit_syscall_entry((test_thread_flag(TIF_32BIT) ? - AUDIT_ARCH_SPARC : - AUDIT_ARCH_SPARC64), - regs->u_regs[UREG_G1], - regs->u_regs[UREG_I0], - regs->u_regs[UREG_I1], - regs->u_regs[UREG_I2], + audit_syscall_entry(regs->u_regs[UREG_G1], regs->u_regs[UREG_I0], + regs->u_regs[UREG_I1], regs->u_regs[UREG_I2], regs->u_regs[UREG_I3]); return ret; diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c index d5c319553fd0874903515106920d283c066d8fbd..9d98e5002a09a483eb87b23bd68b33a6e6f83e05 100644 --- a/arch/sparc/kernel/sun4d_smp.c +++ b/arch/sparc/kernel/sun4d_smp.c @@ -204,7 +204,7 @@ static void __init smp4d_ipi_init(void) void sun4d_ipi_interrupt(void) { - struct sun4d_ipi_work *work = &__get_cpu_var(sun4d_ipi_work); + struct sun4d_ipi_work *work = this_cpu_ptr(&sun4d_ipi_work); if (work->single) { work->single = 0; diff --git a/arch/sparc/kernel/time_64.c b/arch/sparc/kernel/time_64.c index 3fddf64c7fc63ab81bb1ec0599b9cefa790ec6c8..59da0c3ea78879f14420946a06095da5d7628289 100644 --- a/arch/sparc/kernel/time_64.c +++ b/arch/sparc/kernel/time_64.c @@ -765,7 +765,7 @@ void setup_sparc64_timer(void) : /* no outputs */ : "r" (pstate)); - sevt = &__get_cpu_var(sparc64_events); + sevt = this_cpu_ptr(&sparc64_events); memcpy(sevt, &sparc64_clockevent, sizeof(*sevt)); sevt->cpumask = cpumask_of(smp_processor_id()); diff --git a/arch/sparc/kernel/tsb.S b/arch/sparc/kernel/tsb.S index 14158d40ba766d05917482029edd93546078281d..be98685c14c62301250db79791269fb411ea9e0a 100644 --- a/arch/sparc/kernel/tsb.S +++ b/arch/sparc/kernel/tsb.S @@ -162,10 +162,10 @@ tsb_miss_page_table_walk_sun4v_fastpath: nop .previous - rdpr %tl, %g3 - cmp %g3, 1 + rdpr %tl, %g7 + cmp %g7, 1 bne,pn %xcc, winfix_trampoline - nop + mov %g3, %g4 ba,pt %xcc, etrap rd %pc, %g7 call hugetlb_setup diff --git a/arch/sparc/lib/NG4memcpy.S b/arch/sparc/lib/NG4memcpy.S index 9cf2ee01cee371e77749469831a5f9fab9f9fa09..140527a20e7df03cc0a0dd9e6a3438f44b432177 100644 --- a/arch/sparc/lib/NG4memcpy.S +++ b/arch/sparc/lib/NG4memcpy.S @@ -41,6 +41,10 @@ #endif #endif +#if !defined(EX_LD) && !defined(EX_ST) +#define NON_USER_COPY +#endif + #ifndef EX_LD #define EX_LD(x) x #endif @@ -197,9 +201,13 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */ mov EX_RETVAL(%o3), %o0 .Llarge_src_unaligned: +#ifdef NON_USER_COPY + VISEntryHalfFast(.Lmedium_vis_entry_fail) +#else + VISEntryHalf +#endif andn %o2, 0x3f, %o4 sub %o2, %o4, %o2 - VISEntryHalf alignaddr %o1, %g0, %g1 add %o1, %o4, %o1 EX_LD(LOAD(ldd, %g1 + 0x00, %f0)) @@ -240,6 +248,10 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */ nop ba,a,pt %icc, .Lmedium_unaligned +#ifdef NON_USER_COPY +.Lmedium_vis_entry_fail: + or %o0, %o1, %g2 +#endif .Lmedium: LOAD(prefetch, %o1 + 0x40, #n_reads_strong) andcc %g2, 0x7, %g0 diff --git a/arch/sparc/mm/tlb.c b/arch/sparc/mm/tlb.c index b89aba217e3b5105c3fae5737cca2f279db4dc95..9df2190c097e1bb09de8378ca89b19e79c384917 100644 --- a/arch/sparc/mm/tlb.c +++ b/arch/sparc/mm/tlb.c @@ -52,14 +52,14 @@ void flush_tlb_pending(void) void arch_enter_lazy_mmu_mode(void) { - struct tlb_batch *tb = &__get_cpu_var(tlb_batch); + struct tlb_batch *tb = this_cpu_ptr(&tlb_batch); tb->active = 1; } void arch_leave_lazy_mmu_mode(void) { - struct tlb_batch *tb = &__get_cpu_var(tlb_batch); + struct tlb_batch *tb = this_cpu_ptr(&tlb_batch); if (tb->tlb_nr) flush_tlb_pending(); diff --git a/arch/tile/include/asm/irqflags.h b/arch/tile/include/asm/irqflags.h index 71af5747874d6a47b90534bd54a4d56bbcd138ad..60d62a292fce6f3170eb67416e517647d670d8b4 100644 --- a/arch/tile/include/asm/irqflags.h +++ b/arch/tile/include/asm/irqflags.h @@ -140,12 +140,12 @@ extern unsigned int debug_smp_processor_id(void); /* * Read the set of maskable interrupts. - * We avoid the preemption warning here via __this_cpu_ptr since even + * We avoid the preemption warning here via raw_cpu_ptr since even * if irqs are already enabled, it's harmless to read the wrong cpu's * enabled mask. */ #define arch_local_irqs_enabled() \ - (*__this_cpu_ptr(&interrupts_enabled_mask)) + (*raw_cpu_ptr(&interrupts_enabled_mask)) /* Re-enable all maskable interrupts. */ #define arch_local_irq_enable() \ diff --git a/arch/tile/include/asm/mmu_context.h b/arch/tile/include/asm/mmu_context.h index 4734215e2ad451ecb5b9d3e655ccc72f31ac80f0..f67753db1f782cf10e81eb9820a985e42e59e560 100644 --- a/arch/tile/include/asm/mmu_context.h +++ b/arch/tile/include/asm/mmu_context.h @@ -84,7 +84,7 @@ static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *t) * clear any pending DMA interrupts. */ if (current->thread.tile_dma_state.enabled) - install_page_table(mm->pgd, __get_cpu_var(current_asid)); + install_page_table(mm->pgd, __this_cpu_read(current_asid)); #endif } @@ -96,12 +96,12 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, int cpu = smp_processor_id(); /* Pick new ASID. */ - int asid = __get_cpu_var(current_asid) + 1; + int asid = __this_cpu_read(current_asid) + 1; if (asid > max_asid) { asid = min_asid; local_flush_tlb(); } - __get_cpu_var(current_asid) = asid; + __this_cpu_write(current_asid, asid); /* Clear cpu from the old mm, and set it in the new one. */ cpumask_clear_cpu(cpu, mm_cpumask(prev)); diff --git a/arch/tile/kernel/irq.c b/arch/tile/kernel/irq.c index 637f2ffaa5f56159bb7eee1fe0e8b2eda8f97579..ba85765e1436d6aa828af3f74f3a4f80b354bd59 100644 --- a/arch/tile/kernel/irq.c +++ b/arch/tile/kernel/irq.c @@ -73,7 +73,7 @@ static DEFINE_PER_CPU(int, irq_depth); */ void tile_dev_intr(struct pt_regs *regs, int intnum) { - int depth = __get_cpu_var(irq_depth)++; + int depth = __this_cpu_inc_return(irq_depth); unsigned long original_irqs; unsigned long remaining_irqs; struct pt_regs *old_regs; @@ -120,7 +120,7 @@ void tile_dev_intr(struct pt_regs *regs, int intnum) /* Count device irqs; Linux IPIs are counted elsewhere. */ if (irq != IRQ_RESCHEDULE) - __get_cpu_var(irq_stat).irq_dev_intr_count++; + __this_cpu_inc(irq_stat.irq_dev_intr_count); generic_handle_irq(irq); } @@ -130,10 +130,10 @@ void tile_dev_intr(struct pt_regs *regs, int intnum) * including any that were reenabled during interrupt * handling. */ - if (depth == 0) - unmask_irqs(~__get_cpu_var(irq_disable_mask)); + if (depth == 1) + unmask_irqs(~__this_cpu_read(irq_disable_mask)); - __get_cpu_var(irq_depth)--; + __this_cpu_dec(irq_depth); /* * Track time spent against the current process again and @@ -151,7 +151,7 @@ void tile_dev_intr(struct pt_regs *regs, int intnum) static void tile_irq_chip_enable(struct irq_data *d) { get_cpu_var(irq_disable_mask) &= ~(1UL << d->irq); - if (__get_cpu_var(irq_depth) == 0) + if (__this_cpu_read(irq_depth) == 0) unmask_irqs(1UL << d->irq); put_cpu_var(irq_disable_mask); } @@ -197,7 +197,7 @@ static void tile_irq_chip_ack(struct irq_data *d) */ static void tile_irq_chip_eoi(struct irq_data *d) { - if (!(__get_cpu_var(irq_disable_mask) & (1UL << d->irq))) + if (!(__this_cpu_read(irq_disable_mask) & (1UL << d->irq))) unmask_irqs(1UL << d->irq); } diff --git a/arch/tile/kernel/messaging.c b/arch/tile/kernel/messaging.c index 7867266f97169a5dd87fb09508e6833a94ef5b74..ac950be1318e410733627f7ca237c4f6ea9cd4c7 100644 --- a/arch/tile/kernel/messaging.c +++ b/arch/tile/kernel/messaging.c @@ -28,7 +28,7 @@ static DEFINE_PER_CPU(HV_MsgState, msg_state); void init_messaging(void) { /* Allocate storage for messages in kernel space */ - HV_MsgState *state = &__get_cpu_var(msg_state); + HV_MsgState *state = this_cpu_ptr(&msg_state); int rc = hv_register_message_state(state); if (rc != HV_OK) panic("hv_register_message_state: error %d", rc); @@ -96,7 +96,7 @@ void hv_message_intr(struct pt_regs *regs, int intnum) struct hv_driver_cb *cb = (struct hv_driver_cb *)him->intarg; cb->callback(cb, him->intdata); - __get_cpu_var(irq_stat).irq_hv_msg_count++; + __this_cpu_inc(irq_stat.irq_hv_msg_count); } } diff --git a/arch/tile/kernel/perf_event.c b/arch/tile/kernel/perf_event.c index 2bf6c9c135c15e7b44b980a72c0b2f9592073799..bb509cee3b598807eeae1aca6056e7adaed59e19 100644 --- a/arch/tile/kernel/perf_event.c +++ b/arch/tile/kernel/perf_event.c @@ -590,7 +590,7 @@ static int tile_event_set_period(struct perf_event *event) */ static void tile_pmu_stop(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc = &event->hw; int idx = hwc->idx; @@ -616,7 +616,7 @@ static void tile_pmu_stop(struct perf_event *event, int flags) */ static void tile_pmu_start(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int idx = event->hw.idx; if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED))) @@ -650,7 +650,7 @@ static void tile_pmu_start(struct perf_event *event, int flags) */ static int tile_pmu_add(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc; unsigned long mask; int b, max_cnt; @@ -706,7 +706,7 @@ static int tile_pmu_add(struct perf_event *event, int flags) */ static void tile_pmu_del(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int i; /* @@ -880,14 +880,14 @@ static struct pmu tilera_pmu = { int tile_pmu_handle_irq(struct pt_regs *regs, int fault) { struct perf_sample_data data; - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct perf_event *event; struct hw_perf_event *hwc; u64 val; unsigned long status; int bit; - __get_cpu_var(perf_irqs)++; + __this_cpu_inc(perf_irqs); if (!atomic_read(&tile_active_events)) return 0; diff --git a/arch/tile/kernel/process.c b/arch/tile/kernel/process.c index 16ed58948757565639d93dc0417b69ea17269b68..0050cbc1d9de4230cc772413b6fde977954a000d 100644 --- a/arch/tile/kernel/process.c +++ b/arch/tile/kernel/process.c @@ -64,7 +64,7 @@ early_param("idle", idle_setup); void arch_cpu_idle(void) { - __get_cpu_var(irq_stat).idle_timestamp = jiffies; + __this_cpu_write(irq_stat.idle_timestamp, jiffies); _cpu_idle(); } diff --git a/arch/tile/kernel/setup.c b/arch/tile/kernel/setup.c index 112ababa9e5502c6593969d44c976fbd495cf663..b9736ded06f25fb341c5e7d7a248218b803549cc 100644 --- a/arch/tile/kernel/setup.c +++ b/arch/tile/kernel/setup.c @@ -1218,7 +1218,8 @@ static void __init validate_hv(void) * various asid variables to their appropriate initial states. */ asid_range = hv_inquire_asid(0); - __get_cpu_var(current_asid) = min_asid = asid_range.start; + min_asid = asid_range.start; + __this_cpu_write(current_asid, min_asid); max_asid = asid_range.start + asid_range.size - 1; if (hv_confstr(HV_CONFSTR_CHIP_MODEL, (HV_VirtAddr)chip_model, diff --git a/arch/tile/kernel/single_step.c b/arch/tile/kernel/single_step.c index de07fa7d1315e34c93bfa82d995c1393cadcf096..6cb2ce31b5a2d394aa290ca81a103ba824969dca 100644 --- a/arch/tile/kernel/single_step.c +++ b/arch/tile/kernel/single_step.c @@ -740,7 +740,7 @@ static DEFINE_PER_CPU(unsigned long, ss_saved_pc); void gx_singlestep_handle(struct pt_regs *regs, int fault_num) { - unsigned long *ss_pc = &__get_cpu_var(ss_saved_pc); + unsigned long *ss_pc = this_cpu_ptr(&ss_saved_pc); struct thread_info *info = (void *)current_thread_info(); int is_single_step = test_ti_thread_flag(info, TIF_SINGLESTEP); unsigned long control = __insn_mfspr(SPR_SINGLE_STEP_CONTROL_K); @@ -766,7 +766,7 @@ void gx_singlestep_handle(struct pt_regs *regs, int fault_num) void single_step_once(struct pt_regs *regs) { - unsigned long *ss_pc = &__get_cpu_var(ss_saved_pc); + unsigned long *ss_pc = this_cpu_ptr(&ss_saved_pc); unsigned long control = __insn_mfspr(SPR_SINGLE_STEP_CONTROL_K); *ss_pc = regs->pc; diff --git a/arch/tile/kernel/smp.c b/arch/tile/kernel/smp.c index 19eaa62d456a7c0635c9ec407dc3b998b7027bd4..d3c4ed780ce2244cfcab4da5068fb29442ac5786 100644 --- a/arch/tile/kernel/smp.c +++ b/arch/tile/kernel/smp.c @@ -189,7 +189,7 @@ EXPORT_SYMBOL(flush_icache_range); /* Called when smp_send_reschedule() triggers IRQ_RESCHEDULE. */ static irqreturn_t handle_reschedule_ipi(int irq, void *token) { - __get_cpu_var(irq_stat).irq_resched_count++; + __this_cpu_inc(irq_stat.irq_resched_count); scheduler_ipi(); return IRQ_HANDLED; diff --git a/arch/tile/kernel/smpboot.c b/arch/tile/kernel/smpboot.c index 732e9d1386618066ad8cb294e0155a6fcd70417d..0d59a1b60c742b1c719ddfefa9bb6a87c7ad1c0b 100644 --- a/arch/tile/kernel/smpboot.c +++ b/arch/tile/kernel/smpboot.c @@ -41,7 +41,7 @@ void __init smp_prepare_boot_cpu(void) int cpu = smp_processor_id(); set_cpu_online(cpu, 1); set_cpu_present(cpu, 1); - __get_cpu_var(cpu_state) = CPU_ONLINE; + __this_cpu_write(cpu_state, CPU_ONLINE); init_messaging(); } @@ -158,7 +158,7 @@ static void start_secondary(void) /* printk(KERN_DEBUG "Initializing CPU#%d\n", cpuid); */ /* Initialize the current asid for our first page table. */ - __get_cpu_var(current_asid) = min_asid; + __this_cpu_write(current_asid, min_asid); /* Set up this thread as another owner of the init_mm */ atomic_inc(&init_mm.mm_count); @@ -201,7 +201,7 @@ void online_secondary(void) notify_cpu_starting(smp_processor_id()); set_cpu_online(smp_processor_id(), 1); - __get_cpu_var(cpu_state) = CPU_ONLINE; + __this_cpu_write(cpu_state, CPU_ONLINE); /* Set up tile-specific state for this cpu. */ setup_cpu(0); diff --git a/arch/tile/kernel/time.c b/arch/tile/kernel/time.c index c1b362277fb7110a5980979336f097bfb7c44509..b854a1cd0079ebb864efd63791753d83f6455ad9 100644 --- a/arch/tile/kernel/time.c +++ b/arch/tile/kernel/time.c @@ -162,7 +162,7 @@ static DEFINE_PER_CPU(struct clock_event_device, tile_timer) = { void setup_tile_timer(void) { - struct clock_event_device *evt = &__get_cpu_var(tile_timer); + struct clock_event_device *evt = this_cpu_ptr(&tile_timer); /* Fill in fields that are speed-specific. */ clockevents_calc_mult_shift(evt, cycles_per_sec, TILE_MINSEC); @@ -182,7 +182,7 @@ void setup_tile_timer(void) void do_timer_interrupt(struct pt_regs *regs, int fault_num) { struct pt_regs *old_regs = set_irq_regs(regs); - struct clock_event_device *evt = &__get_cpu_var(tile_timer); + struct clock_event_device *evt = this_cpu_ptr(&tile_timer); /* * Mask the timer interrupt here, since we are a oneshot timer @@ -194,7 +194,7 @@ void do_timer_interrupt(struct pt_regs *regs, int fault_num) irq_enter(); /* Track interrupt count. */ - __get_cpu_var(irq_stat).irq_timer_count++; + __this_cpu_inc(irq_stat.irq_timer_count); /* Call the generic timer handler */ evt->event_handler(evt); @@ -235,7 +235,7 @@ cycles_t ns2cycles(unsigned long nsecs) * We do not have to disable preemption here as each core has the same * clock frequency. */ - struct clock_event_device *dev = &__raw_get_cpu_var(tile_timer); + struct clock_event_device *dev = raw_cpu_ptr(&tile_timer); /* * as in clocksource.h and x86's timer.h, we split the calculation diff --git a/arch/tile/mm/highmem.c b/arch/tile/mm/highmem.c index 0dc218294770c23658158ccccb5ca566f60a8939..6aa2f26254471e730e29b0c1f1382c3f7a4d0f64 100644 --- a/arch/tile/mm/highmem.c +++ b/arch/tile/mm/highmem.c @@ -103,7 +103,7 @@ static void kmap_atomic_register(struct page *page, int type, spin_lock(&_lock); /* With interrupts disabled, now fill in the per-cpu info. */ - amp = &__get_cpu_var(amps).per_type[type]; + amp = this_cpu_ptr(&s.per_type[type]); amp->page = page; amp->cpu = smp_processor_id(); amp->va = va; diff --git a/arch/tile/mm/init.c b/arch/tile/mm/init.c index a092e393bd202f2254dc00f1c6461d805419054e..caa270165f864e08a32f513e5c393fca17ad02a8 100644 --- a/arch/tile/mm/init.c +++ b/arch/tile/mm/init.c @@ -593,14 +593,14 @@ static void __init kernel_physical_mapping_init(pgd_t *pgd_base) interrupt_mask_set_mask(-1ULL); rc = flush_and_install_context(__pa(pgtables), init_pgprot((unsigned long)pgtables), - __get_cpu_var(current_asid), + __this_cpu_read(current_asid), cpumask_bits(my_cpu_mask)); interrupt_mask_restore_mask(irqmask); BUG_ON(rc != 0); /* Copy the page table back to the normal swapper_pg_dir. */ memcpy(pgd_base, pgtables, sizeof(pgtables)); - __install_page_table(pgd_base, __get_cpu_var(current_asid), + __install_page_table(pgd_base, __this_cpu_read(current_asid), swapper_pgprot); /* diff --git a/arch/um/Kconfig.common b/arch/um/Kconfig.common index 6915d28cf118f6c406ff53b20802a36e6ddfcbe0..87bc86821bc9b81380f46ac38557ad537fd0a442 100644 --- a/arch/um/Kconfig.common +++ b/arch/um/Kconfig.common @@ -39,7 +39,8 @@ config LOCKDEP_SUPPORT config STACKTRACE_SUPPORT bool - default n + default y + select STACKTRACE config GENERIC_CALIBRATE_DELAY bool diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c index 7d26d9c0b2fb85cd96ca25953f2bb84d4022be9d..f70dd540655de57bdcddf8c0e2ab1fc6e0e611ba 100644 --- a/arch/um/drivers/net_kern.c +++ b/arch/um/drivers/net_kern.c @@ -659,10 +659,6 @@ static int __init eth_setup(char *str) } new = alloc_bootmem(sizeof(*new)); - if (new == NULL) { - printk(KERN_ERR "eth_init : alloc_bootmem failed\n"); - return 1; - } INIT_LIST_HEAD(&new->list); new->index = n; diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c index 3716e69525546c984f8e730a458c6e22a4cc35af..e8ab93c3e638e160e300f016588184b12cc26bd8 100644 --- a/arch/um/drivers/ubd_kern.c +++ b/arch/um/drivers/ubd_kern.c @@ -1277,7 +1277,7 @@ static void do_ubd_request(struct request_queue *q) while(1){ struct ubd *dev = q->queuedata; - if(dev->end_sg == 0){ + if(dev->request == NULL){ struct request *req = blk_fetch_request(q); if(req == NULL) return; @@ -1299,7 +1299,8 @@ static void do_ubd_request(struct request_queue *q) return; } prepare_flush_request(req, io_req); - submit_request(io_req, dev); + if (submit_request(io_req, dev) == false) + return; } while(dev->start_sg < dev->end_sg){ diff --git a/arch/um/include/asm/stacktrace.h b/arch/um/include/asm/stacktrace.h new file mode 100644 index 0000000000000000000000000000000000000000..9a864328c67f54ff658ed878d5f6a78d641af9f2 --- /dev/null +++ b/arch/um/include/asm/stacktrace.h @@ -0,0 +1,42 @@ +#ifndef _ASM_UML_STACKTRACE_H +#define _ASM_UML_STACKTRACE_H + +#include +#include + +struct stack_frame { + struct stack_frame *next_frame; + unsigned long return_address; +}; + +struct stacktrace_ops { + void (*address)(void *data, unsigned long address, int reliable); +}; + +#ifdef CONFIG_FRAME_POINTER +static inline unsigned long +get_frame_pointer(struct task_struct *task, struct pt_regs *segv_regs) +{ + if (!task || task == current) + return segv_regs ? PT_REGS_BP(segv_regs) : current_bp(); + return KSTK_EBP(task); +} +#else +static inline unsigned long +get_frame_pointer(struct task_struct *task, struct pt_regs *segv_regs) +{ + return 0; +} +#endif + +static inline unsigned long +*get_stack_pointer(struct task_struct *task, struct pt_regs *segv_regs) +{ + if (!task || task == current) + return segv_regs ? (unsigned long *)PT_REGS_SP(segv_regs) : current_sp(); + return (unsigned long *)KSTK_ESP(task); +} + +void dump_trace(struct task_struct *tsk, const struct stacktrace_ops *ops, void *data); + +#endif /* _ASM_UML_STACKTRACE_H */ diff --git a/arch/um/include/shared/mem_user.h b/arch/um/include/shared/mem_user.h index 46384acd547b7590bd75e4f564cf203327e2e522..cb84414e3e6663eb280c555d88ec447c5c6cad27 100644 --- a/arch/um/include/shared/mem_user.h +++ b/arch/um/include/shared/mem_user.h @@ -49,7 +49,7 @@ extern int iomem_size; extern int init_mem_user(void); extern void setup_memory(void *entry); extern unsigned long find_iomem(char *driver, unsigned long *len_out); -extern int init_maps(unsigned long physmem, unsigned long iomem, +extern void mem_total_pages(unsigned long physmem, unsigned long iomem, unsigned long highmem); extern unsigned long get_vm(unsigned long len); extern void setup_physmem(unsigned long start, unsigned long usable, diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile index d8b78a03855c6a1ad4c7ada6c96938f5e62ee02a..2d840a070c8bb56fc2f54d1bba6a25843ad59a6a 100644 --- a/arch/um/kernel/Makefile +++ b/arch/um/kernel/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o obj-$(CONFIG_GPROF) += gprof_syms.o obj-$(CONFIG_GCOV) += gmon_syms.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o +obj-$(CONFIG_STACKTRACE) += stacktrace.o USER_OBJS := config.o diff --git a/arch/um/kernel/physmem.c b/arch/um/kernel/physmem.c index 30fdd5d0067b26c91fb8c831da5a1d4008c79e22..549ecf3f5857441c95e4344b6aabc99ce4723f7f 100644 --- a/arch/um/kernel/physmem.c +++ b/arch/um/kernel/physmem.c @@ -22,39 +22,19 @@ EXPORT_SYMBOL(high_physmem); extern unsigned long long physmem_size; -int __init init_maps(unsigned long physmem, unsigned long iomem, +void __init mem_total_pages(unsigned long physmem, unsigned long iomem, unsigned long highmem) { - struct page *p, *map; - unsigned long phys_len, phys_pages, highmem_len, highmem_pages; - unsigned long iomem_len, iomem_pages, total_len, total_pages; - int i; - - phys_pages = physmem >> PAGE_SHIFT; - phys_len = phys_pages * sizeof(struct page); - - iomem_pages = iomem >> PAGE_SHIFT; - iomem_len = iomem_pages * sizeof(struct page); + unsigned long phys_pages, highmem_pages; + unsigned long iomem_pages, total_pages; + phys_pages = physmem >> PAGE_SHIFT; + iomem_pages = iomem >> PAGE_SHIFT; highmem_pages = highmem >> PAGE_SHIFT; - highmem_len = highmem_pages * sizeof(struct page); - - total_pages = phys_pages + iomem_pages + highmem_pages; - total_len = phys_len + iomem_len + highmem_len; - map = alloc_bootmem_low_pages(total_len); - if (map == NULL) - return -ENOMEM; - - for (i = 0; i < total_pages; i++) { - p = &map[i]; - memset(p, 0, sizeof(struct page)); - SetPageReserved(p); - INIT_LIST_HEAD(&p->lru); - } + total_pages = phys_pages + iomem_pages + highmem_pages; max_mapnr = total_pages; - return 0; } void map_memory(unsigned long virt, unsigned long phys, unsigned long len, diff --git a/arch/um/kernel/ptrace.c b/arch/um/kernel/ptrace.c index 694d551c88996dbee7c974487b9299c9f1efc8fe..62435ef003d9f5a68a2a92a5153a7a757a314343 100644 --- a/arch/um/kernel/ptrace.c +++ b/arch/um/kernel/ptrace.c @@ -165,8 +165,7 @@ static void send_sigtrap(struct task_struct *tsk, struct uml_pt_regs *regs, */ void syscall_trace_enter(struct pt_regs *regs) { - audit_syscall_entry(HOST_AUDIT_ARCH, - UPT_SYSCALL_NR(®s->regs), + audit_syscall_entry(UPT_SYSCALL_NR(®s->regs), UPT_SYSCALL_ARG1(®s->regs), UPT_SYSCALL_ARG2(®s->regs), UPT_SYSCALL_ARG3(®s->regs), diff --git a/arch/um/kernel/stacktrace.c b/arch/um/kernel/stacktrace.c new file mode 100644 index 0000000000000000000000000000000000000000..ebe7bcf62684c5312aaec16bbe5cadd4f13ff595 --- /dev/null +++ b/arch/um/kernel/stacktrace.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) + * Copyright (C) 2013 Richard Weinberger + * Copyright (C) 2014 Google Inc., Author: Daniel Walter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +void dump_trace(struct task_struct *tsk, + const struct stacktrace_ops *ops, + void *data) +{ + int reliable = 0; + unsigned long *sp, bp, addr; + struct pt_regs *segv_regs = tsk->thread.segv_regs; + struct stack_frame *frame; + + bp = get_frame_pointer(tsk, segv_regs); + sp = get_stack_pointer(tsk, segv_regs); + + frame = (struct stack_frame *)bp; + while (((long) sp & (THREAD_SIZE-1)) != 0) { + addr = *sp; + if (__kernel_text_address(addr)) { + reliable = 0; + if ((unsigned long) sp == bp + sizeof(long)) { + frame = frame ? frame->next_frame : NULL; + bp = (unsigned long)frame; + reliable = 1; + } + ops->address(data, addr, reliable); + } + sp++; + } +} + +static void save_addr(void *data, unsigned long address, int reliable) +{ + struct stack_trace *trace = data; + + if (!reliable) + return; + if (trace->nr_entries >= trace->max_entries) + return; + + trace->entries[trace->nr_entries++] = address; +} + +static const struct stacktrace_ops dump_ops = { + .address = save_addr +}; + +static void __save_stack_trace(struct task_struct *tsk, struct stack_trace *trace) +{ + dump_trace(tsk, &dump_ops, trace); + if (trace->nr_entries < trace->max_entries) + trace->entries[trace->nr_entries++] = ULONG_MAX; +} + +void save_stack_trace(struct stack_trace *trace) +{ + __save_stack_trace(current, trace); +} +EXPORT_SYMBOL_GPL(save_stack_trace); + +void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) +{ + __save_stack_trace(tsk, trace); +} +EXPORT_SYMBOL_GPL(save_stack_trace_tsk); diff --git a/arch/um/kernel/sysrq.c b/arch/um/kernel/sysrq.c index 799d7e413bf57247f258b25ad6306a91b363e67c..894c8d303cda7c753b000bb3edba035974930b84 100644 --- a/arch/um/kernel/sysrq.c +++ b/arch/um/kernel/sysrq.c @@ -12,57 +12,20 @@ #include #include #include +#include #include -struct stack_frame { - struct stack_frame *next_frame; - unsigned long return_address; -}; - -static void do_stack_trace(unsigned long *sp, unsigned long bp) +static void _print_addr(void *data, unsigned long address, int reliable) { - int reliable; - unsigned long addr; - struct stack_frame *frame = (struct stack_frame *)bp; - - printk(KERN_INFO "Call Trace:\n"); - while (((long) sp & (THREAD_SIZE-1)) != 0) { - addr = *sp; - if (__kernel_text_address(addr)) { - reliable = 0; - if ((unsigned long) sp == bp + sizeof(long)) { - frame = frame ? frame->next_frame : NULL; - bp = (unsigned long)frame; - reliable = 1; - } - - printk(KERN_INFO " [<%08lx>]", addr); - printk(KERN_CONT " %s", reliable ? "" : "? "); - print_symbol(KERN_CONT "%s", addr); - printk(KERN_CONT "\n"); - } - sp++; - } - printk(KERN_INFO "\n"); + pr_info(" [<%08lx>]", address); + pr_cont(" %s", reliable ? "" : "? "); + print_symbol("%s", address); + pr_cont("\n"); } -static unsigned long get_frame_pointer(struct task_struct *task, - struct pt_regs *segv_regs) -{ - if (!task || task == current) - return segv_regs ? PT_REGS_BP(segv_regs) : current_bp(); - else - return KSTK_EBP(task); -} - -static unsigned long *get_stack_pointer(struct task_struct *task, - struct pt_regs *segv_regs) -{ - if (!task || task == current) - return segv_regs ? (unsigned long *)PT_REGS_SP(segv_regs) : current_sp(); - else - return (unsigned long *)KSTK_ESP(task); -} +static const struct stacktrace_ops stackops = { + .address = _print_addr +}; void show_stack(struct task_struct *task, unsigned long *stack) { @@ -71,7 +34,7 @@ void show_stack(struct task_struct *task, unsigned long *stack) int i; if (!segv_regs && os_is_signal_stack()) { - printk(KERN_ERR "Received SIGSEGV in SIGSEGV handler," + pr_err("Received SIGSEGV in SIGSEGV handler," " aborting stack trace!\n"); return; } @@ -83,16 +46,18 @@ void show_stack(struct task_struct *task, unsigned long *stack) if (!stack) sp = get_stack_pointer(task, segv_regs); - printk(KERN_INFO "Stack:\n"); + pr_info("Stack:\n"); stack = sp; for (i = 0; i < 3 * STACKSLOTS_PER_LINE; i++) { if (kstack_end(stack)) break; if (i && ((i % STACKSLOTS_PER_LINE) == 0)) - printk(KERN_CONT "\n"); - printk(KERN_CONT " %08lx", *stack++); + pr_cont("\n"); + pr_cont(" %08lx", *stack++); } - printk(KERN_CONT "\n"); + pr_cont("\n"); - do_stack_trace(sp, bp); + pr_info("Call Trace:\n"); + dump_trace(current, &stackops, NULL); + pr_info("\n"); } diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c index 016adf0985d522ccfb5f22e6d287bb60dfdb4e61..9274eae6ae7b81fb976f059ea9d8afb6b420722a 100644 --- a/arch/um/kernel/um_arch.c +++ b/arch/um/kernel/um_arch.c @@ -348,12 +348,7 @@ int __init linux_main(int argc, char **argv) start_vm = VMALLOC_START; setup_physmem(uml_physmem, uml_reserved, physmem_size, highmem); - if (init_maps(physmem_size, iomem_size, highmem)) { - printf("Failed to allocate mem_map for %Lu bytes of physical " - "memory and %Lu bytes of highmem\n", physmem_size, - highmem); - exit(1); - } + mem_total_pages(physmem_size, iomem_size, highmem); virtmem_size = physmem_size; stack = (unsigned long) argv; diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 711de084ab57114dc8410ea3de0fdbe436e0cf93..8ffba18395c8bb0a93c6a9dc65cfd8739a5937f3 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -198,12 +198,12 @@ sysexit_from_sys_call: #ifdef CONFIG_AUDITSYSCALL .macro auditsys_entry_common - movl %esi,%r9d /* 6th arg: 4th syscall arg */ - movl %edx,%r8d /* 5th arg: 3rd syscall arg */ - /* (already in %ecx) 4th arg: 2nd syscall arg */ - movl %ebx,%edx /* 3rd arg: 1st syscall arg */ - movl %eax,%esi /* 2nd arg: syscall number */ - movl $AUDIT_ARCH_I386,%edi /* 1st arg: audit arch */ + movl %esi,%r8d /* 5th arg: 4th syscall arg */ + movl %ecx,%r9d /*swap with edx*/ + movl %edx,%ecx /* 4th arg: 3rd syscall arg */ + movl %r9d,%edx /* 3rd arg: 2nd syscall arg */ + movl %ebx,%esi /* 2nd arg: 1st syscall arg */ + movl %eax,%edi /* 1st arg: syscall number */ call __audit_syscall_entry movl RAX-ARGOFFSET(%rsp),%eax /* reload syscall number */ cmpq $(IA32_NR_syscalls-1),%rax diff --git a/arch/x86/include/asm/Kbuild b/arch/x86/include/asm/Kbuild index 3bf000fab0aeb51b3fa23bfa1faca8b3a581febc..d55a210a49bf73d08dd3d85327d13aa5547fa39d 100644 --- a/arch/x86/include/asm/Kbuild +++ b/arch/x86/include/asm/Kbuild @@ -6,6 +6,7 @@ genhdr-y += unistd_x32.h generic-y += clkdev.h generic-y += cputime.h +generic-y += dma-contiguous.h generic-y += early_ioremap.h generic-y += mcs_spinlock.h generic-y += scatterlist.h diff --git a/arch/x86/include/asm/debugreg.h b/arch/x86/include/asm/debugreg.h index 4b528a970bd43f0ceed423d4f01819e251d47284..61fd18b83b6c6af777f2cdd2a63d943089de55e1 100644 --- a/arch/x86/include/asm/debugreg.h +++ b/arch/x86/include/asm/debugreg.h @@ -97,11 +97,11 @@ extern void hw_breakpoint_restore(void); DECLARE_PER_CPU(int, debug_stack_usage); static inline void debug_stack_usage_inc(void) { - __get_cpu_var(debug_stack_usage)++; + __this_cpu_inc(debug_stack_usage); } static inline void debug_stack_usage_dec(void) { - __get_cpu_var(debug_stack_usage)--; + __this_cpu_dec(debug_stack_usage); } int is_debug_stack(unsigned long addr); void debug_stack_set_zero(void); diff --git a/arch/x86/include/asm/kprobes.h b/arch/x86/include/asm/kprobes.h index 53cdfb2857abe41f6f83b786a702cfb2fbd41ead..4421b5da409d6424d91eac6fc91231d8e92d3658 100644 --- a/arch/x86/include/asm/kprobes.h +++ b/arch/x86/include/asm/kprobes.h @@ -27,7 +27,6 @@ #include #define __ARCH_WANT_KPROBES_INSN_SLOT -#define ARCH_SUPPORTS_KPROBES_ON_FTRACE struct pt_regs; struct kprobe; diff --git a/arch/x86/include/asm/perf_event_p4.h b/arch/x86/include/asm/perf_event_p4.h index 85e13ccf15c4f29f38ab3c89806346857697f2a4..d725382c2ae0f4c8d7fbe6db7818e9878b652f41 100644 --- a/arch/x86/include/asm/perf_event_p4.h +++ b/arch/x86/include/asm/perf_event_p4.h @@ -189,7 +189,7 @@ static inline int p4_ht_thread(int cpu) { #ifdef CONFIG_SMP if (smp_num_siblings == 2) - return cpu != cpumask_first(__get_cpu_var(cpu_sibling_map)); + return cpu != cpumask_first(this_cpu_cpumask_var_ptr(cpu_sibling_map)); #endif return 0; } diff --git a/arch/x86/include/asm/uv/uv_hub.h b/arch/x86/include/asm/uv/uv_hub.h index c63e925fd6b7382900dcd6805082f354a20934de..a00ad8f2a6578f61db3e322013b93888fbef4afa 100644 --- a/arch/x86/include/asm/uv/uv_hub.h +++ b/arch/x86/include/asm/uv/uv_hub.h @@ -164,7 +164,7 @@ struct uv_hub_info_s { }; DECLARE_PER_CPU(struct uv_hub_info_s, __uv_hub_info); -#define uv_hub_info (&__get_cpu_var(__uv_hub_info)) +#define uv_hub_info this_cpu_ptr(&__uv_hub_info) #define uv_cpu_hub_info(cpu) (&per_cpu(__uv_hub_info, cpu)) /* @@ -601,16 +601,16 @@ struct uv_hub_nmi_s { struct uv_cpu_nmi_s { struct uv_hub_nmi_s *hub; - atomic_t state; - atomic_t pinging; + int state; + int pinging; int queries; int pings; }; -DECLARE_PER_CPU(struct uv_cpu_nmi_s, __uv_cpu_nmi); -#define uv_cpu_nmi (__get_cpu_var(__uv_cpu_nmi)) +DECLARE_PER_CPU(struct uv_cpu_nmi_s, uv_cpu_nmi); + #define uv_hub_nmi (uv_cpu_nmi.hub) -#define uv_cpu_nmi_per(cpu) (per_cpu(__uv_cpu_nmi, cpu)) +#define uv_cpu_nmi_per(cpu) (per_cpu(uv_cpu_nmi, cpu)) #define uv_hub_nmi_per(cpu) (uv_cpu_nmi_per(cpu).hub) /* uv_cpu_nmi_states */ diff --git a/arch/x86/kernel/apb_timer.c b/arch/x86/kernel/apb_timer.c index af5b08ab3b712836e09d7eb9f6cd79919035d22f..5972b108f15a739a06b47276a05d86713d08a3a2 100644 --- a/arch/x86/kernel/apb_timer.c +++ b/arch/x86/kernel/apb_timer.c @@ -146,7 +146,7 @@ static inline int is_apbt_capable(void) static int __init apbt_clockevent_register(void) { struct sfi_timer_table_entry *mtmr; - struct apbt_dev *adev = &__get_cpu_var(cpu_apbt_dev); + struct apbt_dev *adev = this_cpu_ptr(&cpu_apbt_dev); mtmr = sfi_get_mtmr(APBT_CLOCKEVENT0_NUM); if (mtmr == NULL) { @@ -200,7 +200,7 @@ void apbt_setup_secondary_clock(void) if (!cpu) return; - adev = &__get_cpu_var(cpu_apbt_dev); + adev = this_cpu_ptr(&cpu_apbt_dev); if (!adev->timer) { adev->timer = dw_apb_clockevent_init(cpu, adev->name, APBT_CLOCKEVENT_RATING, adev_virt_addr(adev), diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index 24b5894396a06c76b073435f52143f5ca420748f..ba6cc041edb12e23a783fc0c5aaaed788af39dc0 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c @@ -561,7 +561,7 @@ static DEFINE_PER_CPU(struct clock_event_device, lapic_events); */ static void setup_APIC_timer(void) { - struct clock_event_device *levt = &__get_cpu_var(lapic_events); + struct clock_event_device *levt = this_cpu_ptr(&lapic_events); if (this_cpu_has(X86_FEATURE_ARAT)) { lapic_clockevent.features &= ~CLOCK_EVT_FEAT_C3STOP; @@ -696,7 +696,7 @@ calibrate_by_pmtimer(long deltapm, long *delta, long *deltatsc) static int __init calibrate_APIC_clock(void) { - struct clock_event_device *levt = &__get_cpu_var(lapic_events); + struct clock_event_device *levt = this_cpu_ptr(&lapic_events); void (*real_handler)(struct clock_event_device *dev); unsigned long deltaj; long delta, deltatsc; diff --git a/arch/x86/kernel/apic/x2apic_cluster.c b/arch/x86/kernel/apic/x2apic_cluster.c index 6ce600f9bc789e2ccf0fe53bd6cb61091ebd0340..e658f21681c82e1ad8fa28bde084c7933939548b 100644 --- a/arch/x86/kernel/apic/x2apic_cluster.c +++ b/arch/x86/kernel/apic/x2apic_cluster.c @@ -42,7 +42,7 @@ __x2apic_send_IPI_mask(const struct cpumask *mask, int vector, int apic_dest) * We are to modify mask, so we need an own copy * and be sure it's manipulated with irq off. */ - ipi_mask_ptr = __raw_get_cpu_var(ipi_mask); + ipi_mask_ptr = this_cpu_cpumask_var_ptr(ipi_mask); cpumask_copy(ipi_mask_ptr, mask); /* diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 700f958652f8ac1edc9e642b44f6cb61452c48cb..4b4f78c9ba1902ed87127738052135ed54071088 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -964,6 +964,7 @@ static void vgetcpu_set_mode(void) vgetcpu_mode = VGETCPU_LSL; } +#ifdef CONFIG_IA32_EMULATION /* May not be __init: called during resume */ static void syscall32_cpu_init(void) { @@ -975,7 +976,8 @@ static void syscall32_cpu_init(void) wrmsrl(MSR_CSTAR, ia32_cstar_target); } -#endif +#endif /* CONFIG_IA32_EMULATION */ +#endif /* CONFIG_X86_64 */ #ifdef CONFIG_X86_32 void enable_sep_cpu(void) @@ -1198,9 +1200,9 @@ DEFINE_PER_CPU(int, debug_stack_usage); int is_debug_stack(unsigned long addr) { - return __get_cpu_var(debug_stack_usage) || - (addr <= __get_cpu_var(debug_stack_addr) && - addr > (__get_cpu_var(debug_stack_addr) - DEBUG_STKSZ)); + return __this_cpu_read(debug_stack_usage) || + (addr <= __this_cpu_read(debug_stack_addr) && + addr > (__this_cpu_read(debug_stack_addr) - DEBUG_STKSZ)); } NOKPROBE_SYMBOL(is_debug_stack); diff --git a/arch/x86/kernel/cpu/mcheck/mce-inject.c b/arch/x86/kernel/cpu/mcheck/mce-inject.c index 5ac2d1fb28bc590bd9403aea70d8641d3b05e03b..4cfba4371a71f28c4615610475e3eec9e8d9790c 100644 --- a/arch/x86/kernel/cpu/mcheck/mce-inject.c +++ b/arch/x86/kernel/cpu/mcheck/mce-inject.c @@ -83,7 +83,7 @@ static DEFINE_MUTEX(mce_inject_mutex); static int mce_raise_notify(unsigned int cmd, struct pt_regs *regs) { int cpu = smp_processor_id(); - struct mce *m = &__get_cpu_var(injectm); + struct mce *m = this_cpu_ptr(&injectm); if (!cpumask_test_cpu(cpu, mce_inject_cpumask)) return NMI_DONE; cpumask_clear_cpu(cpu, mce_inject_cpumask); @@ -97,7 +97,7 @@ static int mce_raise_notify(unsigned int cmd, struct pt_regs *regs) static void mce_irq_ipi(void *info) { int cpu = smp_processor_id(); - struct mce *m = &__get_cpu_var(injectm); + struct mce *m = this_cpu_ptr(&injectm); if (cpumask_test_cpu(cpu, mce_inject_cpumask) && m->inject_flags & MCJ_EXCEPTION) { @@ -109,7 +109,7 @@ static void mce_irq_ipi(void *info) /* Inject mce on current CPU */ static int raise_local(void) { - struct mce *m = &__get_cpu_var(injectm); + struct mce *m = this_cpu_ptr(&injectm); int context = MCJ_CTX(m->inject_flags); int ret = 0; int cpu = m->extcpu; diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index bd9ccda8087ff5f46ab30bc89225b8912ceaac40..61a9668cebfde8d0ca22ce7dd5bde07f99a2e65f 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -400,7 +400,7 @@ static u64 mce_rdmsrl(u32 msr) if (offset < 0) return 0; - return *(u64 *)((char *)&__get_cpu_var(injectm) + offset); + return *(u64 *)((char *)this_cpu_ptr(&injectm) + offset); } if (rdmsrl_safe(msr, &v)) { @@ -422,7 +422,7 @@ static void mce_wrmsrl(u32 msr, u64 v) int offset = msr_to_offset(msr); if (offset >= 0) - *(u64 *)((char *)&__get_cpu_var(injectm) + offset) = v; + *(u64 *)((char *)this_cpu_ptr(&injectm) + offset) = v; return; } wrmsrl(msr, v); @@ -478,7 +478,7 @@ static DEFINE_PER_CPU(struct mce_ring, mce_ring); /* Runs with CPU affinity in workqueue */ static int mce_ring_empty(void) { - struct mce_ring *r = &__get_cpu_var(mce_ring); + struct mce_ring *r = this_cpu_ptr(&mce_ring); return r->start == r->end; } @@ -490,7 +490,7 @@ static int mce_ring_get(unsigned long *pfn) *pfn = 0; get_cpu(); - r = &__get_cpu_var(mce_ring); + r = this_cpu_ptr(&mce_ring); if (r->start == r->end) goto out; *pfn = r->ring[r->start]; @@ -504,7 +504,7 @@ static int mce_ring_get(unsigned long *pfn) /* Always runs in MCE context with preempt off */ static int mce_ring_add(unsigned long pfn) { - struct mce_ring *r = &__get_cpu_var(mce_ring); + struct mce_ring *r = this_cpu_ptr(&mce_ring); unsigned next; next = (r->end + 1) % MCE_RING_SIZE; @@ -526,7 +526,7 @@ int mce_available(struct cpuinfo_x86 *c) static void mce_schedule_work(void) { if (!mce_ring_empty()) - schedule_work(&__get_cpu_var(mce_work)); + schedule_work(this_cpu_ptr(&mce_work)); } DEFINE_PER_CPU(struct irq_work, mce_irq_work); @@ -551,7 +551,7 @@ static void mce_report_event(struct pt_regs *regs) return; } - irq_work_queue(&__get_cpu_var(mce_irq_work)); + irq_work_queue(this_cpu_ptr(&mce_irq_work)); } /* @@ -1045,7 +1045,7 @@ void do_machine_check(struct pt_regs *regs, long error_code) mce_gather_info(&m, regs); - final = &__get_cpu_var(mces_seen); + final = this_cpu_ptr(&mces_seen); *final = m; memset(valid_banks, 0, sizeof(valid_banks)); @@ -1278,22 +1278,22 @@ static unsigned long (*mce_adjust_timer)(unsigned long interval) = static int cmc_error_seen(void) { - unsigned long *v = &__get_cpu_var(mce_polled_error); + unsigned long *v = this_cpu_ptr(&mce_polled_error); return test_and_clear_bit(0, v); } static void mce_timer_fn(unsigned long data) { - struct timer_list *t = &__get_cpu_var(mce_timer); + struct timer_list *t = this_cpu_ptr(&mce_timer); unsigned long iv; int notify; WARN_ON(smp_processor_id() != data); - if (mce_available(__this_cpu_ptr(&cpu_info))) { + if (mce_available(this_cpu_ptr(&cpu_info))) { machine_check_poll(MCP_TIMESTAMP, - &__get_cpu_var(mce_poll_banks)); + this_cpu_ptr(&mce_poll_banks)); mce_intel_cmci_poll(); } @@ -1323,7 +1323,7 @@ static void mce_timer_fn(unsigned long data) */ void mce_timer_kick(unsigned long interval) { - struct timer_list *t = &__get_cpu_var(mce_timer); + struct timer_list *t = this_cpu_ptr(&mce_timer); unsigned long when = jiffies + interval; unsigned long iv = __this_cpu_read(mce_next_interval); @@ -1659,7 +1659,7 @@ static void mce_start_timer(unsigned int cpu, struct timer_list *t) static void __mcheck_cpu_init_timer(void) { - struct timer_list *t = &__get_cpu_var(mce_timer); + struct timer_list *t = this_cpu_ptr(&mce_timer); unsigned int cpu = smp_processor_id(); setup_timer(t, mce_timer_fn, cpu); @@ -1702,8 +1702,8 @@ void mcheck_cpu_init(struct cpuinfo_x86 *c) __mcheck_cpu_init_generic(); __mcheck_cpu_init_vendor(c); __mcheck_cpu_init_timer(); - INIT_WORK(&__get_cpu_var(mce_work), mce_process_work); - init_irq_work(&__get_cpu_var(mce_irq_work), &mce_irq_work_cb); + INIT_WORK(this_cpu_ptr(&mce_work), mce_process_work); + init_irq_work(this_cpu_ptr(&mce_irq_work), &mce_irq_work_cb); } /* @@ -1955,7 +1955,7 @@ static struct miscdevice mce_chrdev_device = { static void __mce_disable_bank(void *arg) { int bank = *((int *)arg); - __clear_bit(bank, __get_cpu_var(mce_poll_banks)); + __clear_bit(bank, this_cpu_ptr(mce_poll_banks)); cmci_disable_bank(bank); } @@ -2065,7 +2065,7 @@ static void mce_syscore_shutdown(void) static void mce_syscore_resume(void) { __mcheck_cpu_init_generic(); - __mcheck_cpu_init_vendor(__this_cpu_ptr(&cpu_info)); + __mcheck_cpu_init_vendor(raw_cpu_ptr(&cpu_info)); } static struct syscore_ops mce_syscore_ops = { @@ -2080,7 +2080,7 @@ static struct syscore_ops mce_syscore_ops = { static void mce_cpu_restart(void *data) { - if (!mce_available(__this_cpu_ptr(&cpu_info))) + if (!mce_available(raw_cpu_ptr(&cpu_info))) return; __mcheck_cpu_init_generic(); __mcheck_cpu_init_timer(); @@ -2096,14 +2096,14 @@ static void mce_restart(void) /* Toggle features for corrected errors */ static void mce_disable_cmci(void *data) { - if (!mce_available(__this_cpu_ptr(&cpu_info))) + if (!mce_available(raw_cpu_ptr(&cpu_info))) return; cmci_clear(); } static void mce_enable_ce(void *all) { - if (!mce_available(__this_cpu_ptr(&cpu_info))) + if (!mce_available(raw_cpu_ptr(&cpu_info))) return; cmci_reenable(); cmci_recheck(); @@ -2336,7 +2336,7 @@ static void mce_disable_cpu(void *h) unsigned long action = *(unsigned long *)h; int i; - if (!mce_available(__this_cpu_ptr(&cpu_info))) + if (!mce_available(raw_cpu_ptr(&cpu_info))) return; if (!(action & CPU_TASKS_FROZEN)) @@ -2354,7 +2354,7 @@ static void mce_reenable_cpu(void *h) unsigned long action = *(unsigned long *)h; int i; - if (!mce_available(__this_cpu_ptr(&cpu_info))) + if (!mce_available(raw_cpu_ptr(&cpu_info))) return; if (!(action & CPU_TASKS_FROZEN)) diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c index 1e49f8f41276edac73dfc8cbea3417693c6aedd2..5d4999f95aec54c70f237183fedd89ed10cad832 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_amd.c +++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c @@ -310,7 +310,7 @@ static void amd_threshold_interrupt(void) * event. */ machine_check_poll(MCP_TIMESTAMP, - &__get_cpu_var(mce_poll_banks)); + this_cpu_ptr(&mce_poll_banks)); if (high & MASK_OVERFLOW_HI) { rdmsrl(address, m.misc); diff --git a/arch/x86/kernel/cpu/mcheck/mce_intel.c b/arch/x86/kernel/cpu/mcheck/mce_intel.c index 3bdb95ae8c430fa8bacc76a9f644c4abce8ec35e..b3c97bafc1238fedd30f1ec0a3bab2ba7ee05cb8 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_intel.c +++ b/arch/x86/kernel/cpu/mcheck/mce_intel.c @@ -86,7 +86,7 @@ void mce_intel_cmci_poll(void) { if (__this_cpu_read(cmci_storm_state) == CMCI_STORM_NONE) return; - machine_check_poll(MCP_TIMESTAMP, &__get_cpu_var(mce_banks_owned)); + machine_check_poll(MCP_TIMESTAMP, this_cpu_ptr(&mce_banks_owned)); } void mce_intel_hcpu_update(unsigned long cpu) @@ -145,7 +145,7 @@ static void cmci_storm_disable_banks(void) u64 val; raw_spin_lock_irqsave(&cmci_discover_lock, flags); - owned = __get_cpu_var(mce_banks_owned); + owned = this_cpu_ptr(mce_banks_owned); for_each_set_bit(bank, owned, MAX_NR_BANKS) { rdmsrl(MSR_IA32_MCx_CTL2(bank), val); val &= ~MCI_CTL2_CMCI_EN; @@ -195,7 +195,7 @@ static void intel_threshold_interrupt(void) { if (cmci_storm_detect()) return; - machine_check_poll(MCP_TIMESTAMP, &__get_cpu_var(mce_banks_owned)); + machine_check_poll(MCP_TIMESTAMP, this_cpu_ptr(&mce_banks_owned)); mce_notify_irq(); } @@ -206,7 +206,7 @@ static void intel_threshold_interrupt(void) */ static void cmci_discover(int banks) { - unsigned long *owned = (void *)&__get_cpu_var(mce_banks_owned); + unsigned long *owned = (void *)this_cpu_ptr(&mce_banks_owned); unsigned long flags; int i; int bios_wrong_thresh = 0; @@ -228,7 +228,7 @@ static void cmci_discover(int banks) /* Already owned by someone else? */ if (val & MCI_CTL2_CMCI_EN) { clear_bit(i, owned); - __clear_bit(i, __get_cpu_var(mce_poll_banks)); + __clear_bit(i, this_cpu_ptr(mce_poll_banks)); continue; } @@ -252,7 +252,7 @@ static void cmci_discover(int banks) /* Did the enable bit stick? -- the bank supports CMCI */ if (val & MCI_CTL2_CMCI_EN) { set_bit(i, owned); - __clear_bit(i, __get_cpu_var(mce_poll_banks)); + __clear_bit(i, this_cpu_ptr(mce_poll_banks)); /* * We are able to set thresholds for some banks that * had a threshold of 0. This means the BIOS has not @@ -263,7 +263,7 @@ static void cmci_discover(int banks) (val & MCI_CTL2_CMCI_THRESHOLD_MASK)) bios_wrong_thresh = 1; } else { - WARN_ON(!test_bit(i, __get_cpu_var(mce_poll_banks))); + WARN_ON(!test_bit(i, this_cpu_ptr(mce_poll_banks))); } } raw_spin_unlock_irqrestore(&cmci_discover_lock, flags); @@ -284,10 +284,10 @@ void cmci_recheck(void) unsigned long flags; int banks; - if (!mce_available(__this_cpu_ptr(&cpu_info)) || !cmci_supported(&banks)) + if (!mce_available(raw_cpu_ptr(&cpu_info)) || !cmci_supported(&banks)) return; local_irq_save(flags); - machine_check_poll(MCP_TIMESTAMP, &__get_cpu_var(mce_banks_owned)); + machine_check_poll(MCP_TIMESTAMP, this_cpu_ptr(&mce_banks_owned)); local_irq_restore(flags); } @@ -296,12 +296,12 @@ static void __cmci_disable_bank(int bank) { u64 val; - if (!test_bit(bank, __get_cpu_var(mce_banks_owned))) + if (!test_bit(bank, this_cpu_ptr(mce_banks_owned))) return; rdmsrl(MSR_IA32_MCx_CTL2(bank), val); val &= ~MCI_CTL2_CMCI_EN; wrmsrl(MSR_IA32_MCx_CTL2(bank), val); - __clear_bit(bank, __get_cpu_var(mce_banks_owned)); + __clear_bit(bank, this_cpu_ptr(mce_banks_owned)); } /* diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 16c73022306ebcde1c92a208cfa460dea0fc47f0..1b8299dd3d919de522374301b6b33777412e7e7b 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -494,7 +494,7 @@ static int __x86_pmu_event_init(struct perf_event *event) void x86_pmu_disable_all(void) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int idx; for (idx = 0; idx < x86_pmu.num_counters; idx++) { @@ -512,7 +512,7 @@ void x86_pmu_disable_all(void) static void x86_pmu_disable(struct pmu *pmu) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); if (!x86_pmu_initialized()) return; @@ -529,7 +529,7 @@ static void x86_pmu_disable(struct pmu *pmu) void x86_pmu_enable_all(int added) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int idx; for (idx = 0; idx < x86_pmu.num_counters; idx++) { @@ -876,7 +876,7 @@ static void x86_pmu_start(struct perf_event *event, int flags); static void x86_pmu_enable(struct pmu *pmu) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct perf_event *event; struct hw_perf_event *hwc; int i, added = cpuc->n_added; @@ -1030,7 +1030,7 @@ void x86_pmu_enable_event(struct perf_event *event) */ static int x86_pmu_add(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc; int assign[X86_PMC_IDX_MAX]; int n, n0, ret; @@ -1081,7 +1081,7 @@ static int x86_pmu_add(struct perf_event *event, int flags) static void x86_pmu_start(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int idx = event->hw.idx; if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED))) @@ -1160,7 +1160,7 @@ void perf_event_print_debug(void) void x86_pmu_stop(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc = &event->hw; if (__test_and_clear_bit(hwc->idx, cpuc->active_mask)) { @@ -1182,7 +1182,7 @@ void x86_pmu_stop(struct perf_event *event, int flags) static void x86_pmu_del(struct perf_event *event, int flags) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int i; /* @@ -1237,7 +1237,7 @@ int x86_pmu_handle_irq(struct pt_regs *regs) int idx, handled = 0; u64 val; - cpuc = &__get_cpu_var(cpu_hw_events); + cpuc = this_cpu_ptr(&cpu_hw_events); /* * Some chipsets need to unmask the LVTPC in a particular spot @@ -1646,7 +1646,7 @@ static void x86_pmu_cancel_txn(struct pmu *pmu) */ static int x86_pmu_commit_txn(struct pmu *pmu) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int assign[X86_PMC_IDX_MAX]; int n, ret; @@ -2005,7 +2005,7 @@ static unsigned long get_segment_base(unsigned int segment) if (idx > GDT_ENTRIES) return 0; - desc = __this_cpu_ptr(&gdt_page.gdt[0]); + desc = raw_cpu_ptr(gdt_page.gdt); } return get_desc_base(desc + idx); diff --git a/arch/x86/kernel/cpu/perf_event_amd.c b/arch/x86/kernel/cpu/perf_event_amd.c index beeb7cc0704400aaec6d62479331bfe089bef7eb..28926311aac1833b72a932e7108916e25bf4804d 100644 --- a/arch/x86/kernel/cpu/perf_event_amd.c +++ b/arch/x86/kernel/cpu/perf_event_amd.c @@ -699,7 +699,7 @@ __init int amd_pmu_init(void) void amd_pmu_enable_virt(void) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); cpuc->perf_ctr_virt_mask = 0; @@ -711,7 +711,7 @@ EXPORT_SYMBOL_GPL(amd_pmu_enable_virt); void amd_pmu_disable_virt(void) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); /* * We only mask out the Host-only bit so that host-only counting works diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 3851def5057c3dd1b2a7b08acc6641dc918a395a..a73947c53b65aa683f29d61a115d0e6d19a9ec5b 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -1174,7 +1174,7 @@ static inline bool intel_pmu_needs_lbr_smpl(struct perf_event *event) static void intel_pmu_disable_all(void) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0); @@ -1187,7 +1187,7 @@ static void intel_pmu_disable_all(void) static void intel_pmu_enable_all(int added) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); intel_pmu_pebs_enable_all(); intel_pmu_lbr_enable_all(); @@ -1221,7 +1221,7 @@ static void intel_pmu_enable_all(int added) */ static void intel_pmu_nhm_workaround(void) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); static const unsigned long nhm_magic[4] = { 0x4300B5, 0x4300D2, @@ -1320,7 +1320,7 @@ static inline bool event_is_checkpointed(struct perf_event *event) static void intel_pmu_disable_event(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); if (unlikely(hwc->idx == INTEL_PMC_IDX_FIXED_BTS)) { intel_pmu_disable_bts(); @@ -1384,7 +1384,7 @@ static void intel_pmu_enable_fixed(struct hw_perf_event *hwc) static void intel_pmu_enable_event(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); if (unlikely(hwc->idx == INTEL_PMC_IDX_FIXED_BTS)) { if (!__this_cpu_read(cpu_hw_events.enabled)) @@ -1478,7 +1478,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) u64 status; int handled; - cpuc = &__get_cpu_var(cpu_hw_events); + cpuc = this_cpu_ptr(&cpu_hw_events); /* * No known reason to not always do late ACK, @@ -1910,7 +1910,7 @@ EXPORT_SYMBOL_GPL(perf_guest_get_msrs); static struct perf_guest_switch_msr *intel_guest_get_msrs(int *nr) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct perf_guest_switch_msr *arr = cpuc->guest_switch_msrs; arr[0].msr = MSR_CORE_PERF_GLOBAL_CTRL; @@ -1931,7 +1931,7 @@ static struct perf_guest_switch_msr *intel_guest_get_msrs(int *nr) static struct perf_guest_switch_msr *core_guest_get_msrs(int *nr) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct perf_guest_switch_msr *arr = cpuc->guest_switch_msrs; int idx; @@ -1965,7 +1965,7 @@ static void core_pmu_enable_event(struct perf_event *event) static void core_pmu_enable_all(int added) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int idx; for (idx = 0; idx < x86_pmu.num_counters; idx++) { diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index b1553d05a5cb6adee590a2733913e1c774192025..46211bcc813ee812760c15be6c7c11da4bfd52c2 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -474,7 +474,7 @@ void intel_pmu_enable_bts(u64 config) void intel_pmu_disable_bts(void) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); unsigned long debugctlmsr; if (!cpuc->ds) @@ -491,7 +491,7 @@ void intel_pmu_disable_bts(void) int intel_pmu_drain_bts_buffer(void) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct debug_store *ds = cpuc->ds; struct bts_record { u64 from; @@ -669,7 +669,7 @@ struct event_constraint *intel_pebs_constraints(struct perf_event *event) void intel_pmu_pebs_enable(struct perf_event *event) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc = &event->hw; hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT; @@ -684,7 +684,7 @@ void intel_pmu_pebs_enable(struct perf_event *event) void intel_pmu_pebs_disable(struct perf_event *event) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct hw_perf_event *hwc = &event->hw; cpuc->pebs_enabled &= ~(1ULL << hwc->idx); @@ -702,7 +702,7 @@ void intel_pmu_pebs_disable(struct perf_event *event) void intel_pmu_pebs_enable_all(void) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); if (cpuc->pebs_enabled) wrmsrl(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled); @@ -710,7 +710,7 @@ void intel_pmu_pebs_enable_all(void) void intel_pmu_pebs_disable_all(void) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); if (cpuc->pebs_enabled) wrmsrl(MSR_IA32_PEBS_ENABLE, 0); @@ -718,7 +718,7 @@ void intel_pmu_pebs_disable_all(void) static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); unsigned long from = cpuc->lbr_entries[0].from; unsigned long old_to, to = cpuc->lbr_entries[0].to; unsigned long ip = regs->ip; @@ -829,7 +829,7 @@ static void __intel_pmu_pebs_event(struct perf_event *event, * We cast to the biggest pebs_record but are careful not to * unconditionally access the 'extra' entries. */ - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct pebs_record_hsw *pebs = __pebs; struct perf_sample_data data; struct pt_regs regs; @@ -916,7 +916,7 @@ static void __intel_pmu_pebs_event(struct perf_event *event, static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct debug_store *ds = cpuc->ds; struct perf_event *event = cpuc->events[0]; /* PMC0 only */ struct pebs_record_core *at, *top; @@ -957,7 +957,7 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct debug_store *ds = cpuc->ds; struct perf_event *event = NULL; void *at, *top; diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index 4af10617de3389040f28a24dac6875ce933e359a..45fa730a5283255b3c49b1360b6c7c61a34ba513 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -133,7 +133,7 @@ static void intel_pmu_lbr_filter(struct cpu_hw_events *cpuc); static void __intel_pmu_lbr_enable(void) { u64 debugctl; - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); if (cpuc->lbr_sel) wrmsrl(MSR_LBR_SELECT, cpuc->lbr_sel->config); @@ -183,7 +183,7 @@ void intel_pmu_lbr_reset(void) void intel_pmu_lbr_enable(struct perf_event *event) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); if (!x86_pmu.lbr_nr) return; @@ -203,7 +203,7 @@ void intel_pmu_lbr_enable(struct perf_event *event) void intel_pmu_lbr_disable(struct perf_event *event) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); if (!x86_pmu.lbr_nr) return; @@ -220,7 +220,7 @@ void intel_pmu_lbr_disable(struct perf_event *event) void intel_pmu_lbr_enable_all(void) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); if (cpuc->lbr_users) __intel_pmu_lbr_enable(); @@ -228,7 +228,7 @@ void intel_pmu_lbr_enable_all(void) void intel_pmu_lbr_disable_all(void) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); if (cpuc->lbr_users) __intel_pmu_lbr_disable(); @@ -332,7 +332,7 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) void intel_pmu_lbr_read(void) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); if (!cpuc->lbr_users) return; diff --git a/arch/x86/kernel/cpu/perf_event_intel_rapl.c b/arch/x86/kernel/cpu/perf_event_intel_rapl.c index 619f7699487aa1ec60f5a2687bede8e19d0a2c7a..d64f275fe274a14df186eb31a247f04d683058eb 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_rapl.c +++ b/arch/x86/kernel/cpu/perf_event_intel_rapl.c @@ -135,7 +135,7 @@ static inline u64 rapl_scale(u64 v) * or use ldexp(count, -32). * Watts = Joules/Time delta */ - return v << (32 - __get_cpu_var(rapl_pmu)->hw_unit); + return v << (32 - __this_cpu_read(rapl_pmu->hw_unit)); } static u64 rapl_event_update(struct perf_event *event) @@ -187,7 +187,7 @@ static void rapl_stop_hrtimer(struct rapl_pmu *pmu) static enum hrtimer_restart rapl_hrtimer_handle(struct hrtimer *hrtimer) { - struct rapl_pmu *pmu = __get_cpu_var(rapl_pmu); + struct rapl_pmu *pmu = __this_cpu_read(rapl_pmu); struct perf_event *event; unsigned long flags; @@ -234,7 +234,7 @@ static void __rapl_pmu_event_start(struct rapl_pmu *pmu, static void rapl_pmu_event_start(struct perf_event *event, int mode) { - struct rapl_pmu *pmu = __get_cpu_var(rapl_pmu); + struct rapl_pmu *pmu = __this_cpu_read(rapl_pmu); unsigned long flags; spin_lock_irqsave(&pmu->lock, flags); @@ -244,7 +244,7 @@ static void rapl_pmu_event_start(struct perf_event *event, int mode) static void rapl_pmu_event_stop(struct perf_event *event, int mode) { - struct rapl_pmu *pmu = __get_cpu_var(rapl_pmu); + struct rapl_pmu *pmu = __this_cpu_read(rapl_pmu); struct hw_perf_event *hwc = &event->hw; unsigned long flags; @@ -278,7 +278,7 @@ static void rapl_pmu_event_stop(struct perf_event *event, int mode) static int rapl_pmu_event_add(struct perf_event *event, int mode) { - struct rapl_pmu *pmu = __get_cpu_var(rapl_pmu); + struct rapl_pmu *pmu = __this_cpu_read(rapl_pmu); struct hw_perf_event *hwc = &event->hw; unsigned long flags; @@ -696,7 +696,7 @@ static int __init rapl_pmu_init(void) return -1; } - pmu = __get_cpu_var(rapl_pmu); + pmu = __this_cpu_read(rapl_pmu); pr_info("RAPL PMU detected, hw unit 2^-%d Joules," " API unit is 2^-32 Joules," diff --git a/arch/x86/kernel/cpu/perf_event_knc.c b/arch/x86/kernel/cpu/perf_event_knc.c index 838fa8772c628822cd3bd26b6949af47886fbd4f..5b0c232d1ee62e3e50223ba1d8ac27364e2d1ad5 100644 --- a/arch/x86/kernel/cpu/perf_event_knc.c +++ b/arch/x86/kernel/cpu/perf_event_knc.c @@ -217,7 +217,7 @@ static int knc_pmu_handle_irq(struct pt_regs *regs) int bit, loops; u64 status; - cpuc = &__get_cpu_var(cpu_hw_events); + cpuc = this_cpu_ptr(&cpu_hw_events); knc_pmu_disable_all(); diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index 5d466b7d8609814355c90e2cf09ced760debd458..f2e56783af3d83b89de8fa39ae40693cbac2f375 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -915,7 +915,7 @@ static inline void p4_pmu_disable_event(struct perf_event *event) static void p4_pmu_disable_all(void) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int idx; for (idx = 0; idx < x86_pmu.num_counters; idx++) { @@ -984,7 +984,7 @@ static void p4_pmu_enable_event(struct perf_event *event) static void p4_pmu_enable_all(int added) { - struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int idx; for (idx = 0; idx < x86_pmu.num_counters; idx++) { @@ -1004,7 +1004,7 @@ static int p4_pmu_handle_irq(struct pt_regs *regs) int idx, handled = 0; u64 val; - cpuc = &__get_cpu_var(cpu_hw_events); + cpuc = this_cpu_ptr(&cpu_hw_events); for (idx = 0; idx < x86_pmu.num_counters; idx++) { int overflow; diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c index a618fcd2c07d3d54062a8558a7f9941b16859203..f5ab56d1428718f6327c91012d5373cc200438a2 100644 --- a/arch/x86/kernel/crash.c +++ b/arch/x86/kernel/crash.c @@ -237,7 +237,7 @@ static void fill_up_crash_elf_data(struct crash_elf_data *ced, ced->max_nr_ranges++; /* If crashk_low_res is not 0, another range split possible */ - if (crashk_low_res.end != 0) + if (crashk_low_res.end) ced->max_nr_ranges++; } @@ -335,9 +335,11 @@ static int elf_header_exclude_ranges(struct crash_elf_data *ced, if (ret) return ret; - ret = exclude_mem_range(cmem, crashk_low_res.start, crashk_low_res.end); - if (ret) - return ret; + if (crashk_low_res.end) { + ret = exclude_mem_range(cmem, crashk_low_res.start, crashk_low_res.end); + if (ret) + return ret; + } /* Exclude GART region */ if (ced->gart_end) { diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index 4b0e1dfa222627cd5a36fb7b8f51234508822536..b553ed89e5f5e97346ac460934ce6e016b45455f 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S @@ -449,12 +449,11 @@ sysenter_audit: jnz syscall_trace_entry addl $4,%esp CFI_ADJUST_CFA_OFFSET -4 - /* %esi already in 8(%esp) 6th arg: 4th syscall arg */ - /* %edx already in 4(%esp) 5th arg: 3rd syscall arg */ - /* %ecx already in 0(%esp) 4th arg: 2nd syscall arg */ - movl %ebx,%ecx /* 3rd arg: 1st syscall arg */ - movl %eax,%edx /* 2nd arg: syscall number */ - movl $AUDIT_ARCH_I386,%eax /* 1st arg: audit arch */ + movl %esi,4(%esp) /* 5th arg: 4th syscall arg */ + movl %edx,(%esp) /* 4th arg: 3rd syscall arg */ + /* %ecx already in %ecx 3rd arg: 2nd syscall arg */ + movl %ebx,%edx /* 2nd arg: 1st syscall arg */ + /* %eax already in %eax 1st arg: syscall number */ call __audit_syscall_entry pushl_cfi %ebx movl PT_EAX(%esp),%eax /* reload syscall number */ diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c index 5f9cf20cdb68025ae7175860fba5abab136ef9f7..3d5fb509bdebc300af21de5079b7ed61c16a54e4 100644 --- a/arch/x86/kernel/hw_breakpoint.c +++ b/arch/x86/kernel/hw_breakpoint.c @@ -108,7 +108,7 @@ int arch_install_hw_breakpoint(struct perf_event *bp) int i; for (i = 0; i < HBP_NUM; i++) { - struct perf_event **slot = &__get_cpu_var(bp_per_reg[i]); + struct perf_event **slot = this_cpu_ptr(&bp_per_reg[i]); if (!*slot) { *slot = bp; @@ -122,7 +122,7 @@ int arch_install_hw_breakpoint(struct perf_event *bp) set_debugreg(info->address, i); __this_cpu_write(cpu_debugreg[i], info->address); - dr7 = &__get_cpu_var(cpu_dr7); + dr7 = this_cpu_ptr(&cpu_dr7); *dr7 |= encode_dr7(i, info->len, info->type); set_debugreg(*dr7, 7); @@ -146,7 +146,7 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp) int i; for (i = 0; i < HBP_NUM; i++) { - struct perf_event **slot = &__get_cpu_var(bp_per_reg[i]); + struct perf_event **slot = this_cpu_ptr(&bp_per_reg[i]); if (*slot == bp) { *slot = NULL; @@ -157,7 +157,7 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp) if (WARN_ONCE(i == HBP_NUM, "Can't find any breakpoint slot")) return; - dr7 = &__get_cpu_var(cpu_dr7); + dr7 = this_cpu_ptr(&cpu_dr7); *dr7 &= ~__encode_dr7(i, info->len, info->type); set_debugreg(*dr7, 7); diff --git a/arch/x86/kernel/irq_64.c b/arch/x86/kernel/irq_64.c index 4d1c746892eb9a9aae2e38dedfec88868f347cff..e4b503d5558c5d435eddc3c3c5be8c55f1de558d 100644 --- a/arch/x86/kernel/irq_64.c +++ b/arch/x86/kernel/irq_64.c @@ -52,13 +52,13 @@ static inline void stack_overflow_check(struct pt_regs *regs) regs->sp <= curbase + THREAD_SIZE) return; - irq_stack_top = (u64)__get_cpu_var(irq_stack_union.irq_stack) + + irq_stack_top = (u64)this_cpu_ptr(irq_stack_union.irq_stack) + STACK_TOP_MARGIN; - irq_stack_bottom = (u64)__get_cpu_var(irq_stack_ptr); + irq_stack_bottom = (u64)__this_cpu_read(irq_stack_ptr); if (regs->sp >= irq_stack_top && regs->sp <= irq_stack_bottom) return; - oist = &__get_cpu_var(orig_ist); + oist = this_cpu_ptr(&orig_ist); estack_top = (u64)oist->ist[0] - EXCEPTION_STKSZ + STACK_TOP_MARGIN; estack_bottom = (u64)oist->ist[N_EXCEPTION_STACKS - 1]; if (regs->sp >= estack_top && regs->sp <= estack_bottom) diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c index 9642b9b33655a739880fd5105473c81ff1696c9e..ca05f86481aace3a37cd6bf3fbe0e7d7ce3bb33a 100644 --- a/arch/x86/kernel/kexec-bzimage64.c +++ b/arch/x86/kernel/kexec-bzimage64.c @@ -26,6 +26,7 @@ #include #include #include +#include #define MAX_ELFCOREHDR_STR_LEN 30 /* elfcorehdr=0x<64bit-value> */ @@ -267,7 +268,7 @@ setup_boot_parameters(struct kimage *image, struct boot_params *params, return ret; } -int bzImage64_probe(const char *buf, unsigned long len) +static int bzImage64_probe(const char *buf, unsigned long len) { int ret = -ENOEXEC; struct setup_header *header; @@ -325,10 +326,10 @@ int bzImage64_probe(const char *buf, unsigned long len) return ret; } -void *bzImage64_load(struct kimage *image, char *kernel, - unsigned long kernel_len, char *initrd, - unsigned long initrd_len, char *cmdline, - unsigned long cmdline_len) +static void *bzImage64_load(struct kimage *image, char *kernel, + unsigned long kernel_len, char *initrd, + unsigned long initrd_len, char *cmdline, + unsigned long cmdline_len) { struct setup_header *header; @@ -514,7 +515,7 @@ void *bzImage64_load(struct kimage *image, char *kernel, } /* This cleanup function is called after various segments have been loaded */ -int bzImage64_cleanup(void *loader_data) +static int bzImage64_cleanup(void *loader_data) { struct bzimage64_data *ldata = loader_data; @@ -528,7 +529,7 @@ int bzImage64_cleanup(void *loader_data) } #ifdef CONFIG_KEXEC_BZIMAGE_VERIFY_SIG -int bzImage64_verify_sig(const char *kernel, unsigned long kernel_len) +static int bzImage64_verify_sig(const char *kernel, unsigned long kernel_len) { bool trusted; int ret; diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c index 3dd8e2c4d74a9ed4a124baf36a83de78be1be02d..f6945bef2cd10e7b1cbf439cb5ae89cc614bf47c 100644 --- a/arch/x86/kernel/kvm.c +++ b/arch/x86/kernel/kvm.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -243,9 +244,9 @@ u32 kvm_read_and_reset_pf_reason(void) { u32 reason = 0; - if (__get_cpu_var(apf_reason).enabled) { - reason = __get_cpu_var(apf_reason).reason; - __get_cpu_var(apf_reason).reason = 0; + if (__this_cpu_read(apf_reason.enabled)) { + reason = __this_cpu_read(apf_reason.reason); + __this_cpu_write(apf_reason.reason, 0); } return reason; @@ -318,7 +319,7 @@ static void kvm_guest_apic_eoi_write(u32 reg, u32 val) * there's no need for lock or memory barriers. * An optimization barrier is implied in apic write. */ - if (__test_and_clear_bit(KVM_PV_EOI_BIT, &__get_cpu_var(kvm_apic_eoi))) + if (__test_and_clear_bit(KVM_PV_EOI_BIT, this_cpu_ptr(&kvm_apic_eoi))) return; apic_write(APIC_EOI, APIC_EOI_ACK); } @@ -329,13 +330,13 @@ void kvm_guest_cpu_init(void) return; if (kvm_para_has_feature(KVM_FEATURE_ASYNC_PF) && kvmapf) { - u64 pa = slow_virt_to_phys(&__get_cpu_var(apf_reason)); + u64 pa = slow_virt_to_phys(this_cpu_ptr(&apf_reason)); #ifdef CONFIG_PREEMPT pa |= KVM_ASYNC_PF_SEND_ALWAYS; #endif wrmsrl(MSR_KVM_ASYNC_PF_EN, pa | KVM_ASYNC_PF_ENABLED); - __get_cpu_var(apf_reason).enabled = 1; + __this_cpu_write(apf_reason.enabled, 1); printk(KERN_INFO"KVM setup async PF for cpu %d\n", smp_processor_id()); } @@ -344,8 +345,8 @@ void kvm_guest_cpu_init(void) unsigned long pa; /* Size alignment is implied but just to make it explicit. */ BUILD_BUG_ON(__alignof__(kvm_apic_eoi) < 4); - __get_cpu_var(kvm_apic_eoi) = 0; - pa = slow_virt_to_phys(&__get_cpu_var(kvm_apic_eoi)) + __this_cpu_write(kvm_apic_eoi, 0); + pa = slow_virt_to_phys(this_cpu_ptr(&kvm_apic_eoi)) | KVM_MSR_ENABLED; wrmsrl(MSR_KVM_PV_EOI_EN, pa); } @@ -356,11 +357,11 @@ void kvm_guest_cpu_init(void) static void kvm_pv_disable_apf(void) { - if (!__get_cpu_var(apf_reason).enabled) + if (!__this_cpu_read(apf_reason.enabled)) return; wrmsrl(MSR_KVM_ASYNC_PF_EN, 0); - __get_cpu_var(apf_reason).enabled = 0; + __this_cpu_write(apf_reason.enabled, 0); printk(KERN_INFO"Unregister pv shared memory for cpu %d\n", smp_processor_id()); @@ -499,6 +500,13 @@ void __init kvm_guest_init(void) #else kvm_guest_cpu_init(); #endif + + /* + * Hard lockup detection is enabled by default. Disable it, as guests + * can get false positives too easily, for example if the host is + * overcommitted. + */ + watchdog_enable_hardlockup_detector(false); } static noinline uint32_t __kvm_cpuid_base(void) @@ -716,7 +724,7 @@ __visible void kvm_lock_spinning(struct arch_spinlock *lock, __ticket_t want) if (in_nmi()) return; - w = &__get_cpu_var(klock_waiting); + w = this_cpu_ptr(&klock_waiting); cpu = smp_processor_id(); start = spin_time_start(); diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 29576c244699f0632e8d747ab8b8b8461bd4670c..749b0e423419fee7b97698e725dc6d665c1aa828 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -1445,12 +1445,12 @@ static void do_audit_syscall_entry(struct pt_regs *regs, u32 arch) { #ifdef CONFIG_X86_64 if (arch == AUDIT_ARCH_X86_64) { - audit_syscall_entry(arch, regs->orig_ax, regs->di, + audit_syscall_entry(regs->orig_ax, regs->di, regs->si, regs->dx, regs->r10); } else #endif { - audit_syscall_entry(arch, regs->orig_ax, regs->bx, + audit_syscall_entry(regs->orig_ax, regs->bx, regs->cx, regs->dx, regs->si); } } diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index f7f6a4a157a6f8cc8cbeec4e92d718f5c275c86d..65510f624dfe2a42571bc22e73d8755a23e801e5 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -670,7 +670,7 @@ static int svm_hardware_enable(void) if (static_cpu_has(X86_FEATURE_TSCRATEMSR)) { wrmsrl(MSR_AMD64_TSC_RATIO, TSC_RATIO_DEFAULT); - __get_cpu_var(current_tsc_ratio) = TSC_RATIO_DEFAULT; + __this_cpu_write(current_tsc_ratio, TSC_RATIO_DEFAULT); } @@ -1313,8 +1313,8 @@ static void svm_vcpu_load(struct kvm_vcpu *vcpu, int cpu) rdmsrl(host_save_user_msrs[i], svm->host_user_msrs[i]); if (static_cpu_has(X86_FEATURE_TSCRATEMSR) && - svm->tsc_ratio != __get_cpu_var(current_tsc_ratio)) { - __get_cpu_var(current_tsc_ratio) = svm->tsc_ratio; + svm->tsc_ratio != __this_cpu_read(current_tsc_ratio)) { + __this_cpu_write(current_tsc_ratio, svm->tsc_ratio); wrmsrl(MSR_AMD64_TSC_RATIO, svm->tsc_ratio); } } diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 04fa1b8298c8c42103ae896b68ee4611e52da78c..0acac81f198b0b03c83b4f4f9ef86178fa84f821 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -472,6 +472,7 @@ struct vcpu_vmx { int gs_ldt_reload_needed; int fs_reload_needed; u64 msr_host_bndcfgs; + unsigned long vmcs_host_cr4; /* May not match real cr4 */ } host_state; struct { int vm86_active; @@ -1626,7 +1627,7 @@ static void reload_tss(void) /* * VT restores TR but not its size. Useless. */ - struct desc_ptr *gdt = &__get_cpu_var(host_gdt); + struct desc_ptr *gdt = this_cpu_ptr(&host_gdt); struct desc_struct *descs; descs = (void *)gdt->address; @@ -1672,7 +1673,7 @@ static bool update_transition_efer(struct vcpu_vmx *vmx, int efer_offset) static unsigned long segment_base(u16 selector) { - struct desc_ptr *gdt = &__get_cpu_var(host_gdt); + struct desc_ptr *gdt = this_cpu_ptr(&host_gdt); struct desc_struct *d; unsigned long table_base; unsigned long v; @@ -1802,7 +1803,7 @@ static void __vmx_load_host_state(struct vcpu_vmx *vmx) */ if (!user_has_fpu() && !vmx->vcpu.guest_fpu_loaded) stts(); - load_gdt(&__get_cpu_var(host_gdt)); + load_gdt(this_cpu_ptr(&host_gdt)); } static void vmx_load_host_state(struct vcpu_vmx *vmx) @@ -1832,7 +1833,7 @@ static void vmx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) } if (vmx->loaded_vmcs->cpu != cpu) { - struct desc_ptr *gdt = &__get_cpu_var(host_gdt); + struct desc_ptr *gdt = this_cpu_ptr(&host_gdt); unsigned long sysenter_esp; kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu); @@ -2771,7 +2772,7 @@ static int hardware_enable(void) ept_sync_global(); } - native_store_gdt(&__get_cpu_var(host_gdt)); + native_store_gdt(this_cpu_ptr(&host_gdt)); return 0; } @@ -4267,11 +4268,16 @@ static void vmx_set_constant_host_state(struct vcpu_vmx *vmx) u32 low32, high32; unsigned long tmpl; struct desc_ptr dt; + unsigned long cr4; vmcs_writel(HOST_CR0, read_cr0() & ~X86_CR0_TS); /* 22.2.3 */ - vmcs_writel(HOST_CR4, read_cr4()); /* 22.2.3, 22.2.5 */ vmcs_writel(HOST_CR3, read_cr3()); /* 22.2.3 FIXME: shadow tables */ + /* Save the most likely value for this task's CR4 in the VMCS. */ + cr4 = read_cr4(); + vmcs_writel(HOST_CR4, cr4); /* 22.2.3, 22.2.5 */ + vmx->host_state.vmcs_host_cr4 = cr4; + vmcs_write16(HOST_CS_SELECTOR, __KERNEL_CS); /* 22.2.4 */ #ifdef CONFIG_X86_64 /* @@ -7514,7 +7520,7 @@ static void atomic_switch_perf_msrs(struct vcpu_vmx *vmx) static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); - unsigned long debugctlmsr; + unsigned long debugctlmsr, cr4; /* Record the guest's net vcpu time for enforced NMI injections. */ if (unlikely(!cpu_has_virtual_nmis() && vmx->soft_vnmi_blocked)) @@ -7540,6 +7546,12 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu) if (test_bit(VCPU_REGS_RIP, (unsigned long *)&vcpu->arch.regs_dirty)) vmcs_writel(GUEST_RIP, vcpu->arch.regs[VCPU_REGS_RIP]); + cr4 = read_cr4(); + if (unlikely(cr4 != vmx->host_state.vmcs_host_cr4)) { + vmcs_writel(HOST_CR4, cr4); + vmx->host_state.vmcs_host_cr4 = cr4; + } + /* When single-stepping over STI and MOV SS, we must clear the * corresponding interruptibility bits in the guest state. Otherwise * vmentry fails as it then expects bit 14 (BS) in pending debug diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 5430e4b0af29194af597902858c99e11250a3cfa..34c8f94331f83dad10ff80f6b3f1fafa861b9ed6 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1559,7 +1559,7 @@ static int kvm_guest_time_update(struct kvm_vcpu *v) /* Keep irq disabled to prevent changes to the clock */ local_irq_save(flags); - this_tsc_khz = __get_cpu_var(cpu_tsc_khz); + this_tsc_khz = __this_cpu_read(cpu_tsc_khz); if (unlikely(this_tsc_khz == 0)) { local_irq_restore(flags); kvm_make_request(KVM_REQ_CLOCK_UPDATE, v); diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c index baff1da354e0ecfaf371b4e9f30ac6533d657a28..af78e50ca6cee7e4b7293075171986ab6f7166d5 100644 --- a/arch/x86/mm/ioremap.c +++ b/arch/x86/mm/ioremap.c @@ -86,6 +86,7 @@ static void __iomem *__ioremap_caller(resource_size_t phys_addr, pgprot_t prot; int retval; void __iomem *ret_addr; + int ram_region; /* Don't allow wraparound or zero size */ last_addr = phys_addr + size - 1; @@ -108,12 +109,23 @@ static void __iomem *__ioremap_caller(resource_size_t phys_addr, /* * Don't allow anybody to remap normal RAM that we're using.. */ - pfn = phys_addr >> PAGE_SHIFT; - last_pfn = last_addr >> PAGE_SHIFT; - if (walk_system_ram_range(pfn, last_pfn - pfn + 1, NULL, - __ioremap_check_ram) == 1) + /* First check if whole region can be identified as RAM or not */ + ram_region = region_is_ram(phys_addr, size); + if (ram_region > 0) { + WARN_ONCE(1, "ioremap on RAM at 0x%lx - 0x%lx\n", + (unsigned long int)phys_addr, + (unsigned long int)last_addr); return NULL; + } + /* If could not be identified(-1), check page by page */ + if (ram_region < 0) { + pfn = phys_addr >> PAGE_SHIFT; + last_pfn = last_addr >> PAGE_SHIFT; + if (walk_system_ram_range(pfn, last_pfn - pfn + 1, NULL, + __ioremap_check_ram) == 1) + return NULL; + } /* * Mappings have to be page-aligned */ diff --git a/arch/x86/mm/kmemcheck/kmemcheck.c b/arch/x86/mm/kmemcheck/kmemcheck.c index dd89a13f1051adcb18d3565a495a7d34b63b4f5a..b4f2e7e9e90785534e10d017a94e787aa5e8fc03 100644 --- a/arch/x86/mm/kmemcheck/kmemcheck.c +++ b/arch/x86/mm/kmemcheck/kmemcheck.c @@ -140,7 +140,7 @@ static DEFINE_PER_CPU(struct kmemcheck_context, kmemcheck_context); bool kmemcheck_active(struct pt_regs *regs) { - struct kmemcheck_context *data = &__get_cpu_var(kmemcheck_context); + struct kmemcheck_context *data = this_cpu_ptr(&kmemcheck_context); return data->balance > 0; } @@ -148,7 +148,7 @@ bool kmemcheck_active(struct pt_regs *regs) /* Save an address that needs to be shown/hidden */ static void kmemcheck_save_addr(unsigned long addr) { - struct kmemcheck_context *data = &__get_cpu_var(kmemcheck_context); + struct kmemcheck_context *data = this_cpu_ptr(&kmemcheck_context); BUG_ON(data->n_addrs >= ARRAY_SIZE(data->addr)); data->addr[data->n_addrs++] = addr; @@ -156,7 +156,7 @@ static void kmemcheck_save_addr(unsigned long addr) static unsigned int kmemcheck_show_all(void) { - struct kmemcheck_context *data = &__get_cpu_var(kmemcheck_context); + struct kmemcheck_context *data = this_cpu_ptr(&kmemcheck_context); unsigned int i; unsigned int n; @@ -169,7 +169,7 @@ static unsigned int kmemcheck_show_all(void) static unsigned int kmemcheck_hide_all(void) { - struct kmemcheck_context *data = &__get_cpu_var(kmemcheck_context); + struct kmemcheck_context *data = this_cpu_ptr(&kmemcheck_context); unsigned int i; unsigned int n; @@ -185,7 +185,7 @@ static unsigned int kmemcheck_hide_all(void) */ void kmemcheck_show(struct pt_regs *regs) { - struct kmemcheck_context *data = &__get_cpu_var(kmemcheck_context); + struct kmemcheck_context *data = this_cpu_ptr(&kmemcheck_context); BUG_ON(!irqs_disabled()); @@ -226,7 +226,7 @@ void kmemcheck_show(struct pt_regs *regs) */ void kmemcheck_hide(struct pt_regs *regs) { - struct kmemcheck_context *data = &__get_cpu_var(kmemcheck_context); + struct kmemcheck_context *data = this_cpu_ptr(&kmemcheck_context); int n; BUG_ON(!irqs_disabled()); @@ -528,7 +528,7 @@ static void kmemcheck_access(struct pt_regs *regs, const uint8_t *insn_primary; unsigned int size; - struct kmemcheck_context *data = &__get_cpu_var(kmemcheck_context); + struct kmemcheck_context *data = this_cpu_ptr(&kmemcheck_context); /* Recursive fault -- ouch. */ if (data->busy) { diff --git a/arch/x86/mm/numa.c b/arch/x86/mm/numa.c index d221374d5ce82fa769dc31d864a4b72145ae8b42..1a883705a12a8a12410914be93b2ee65807cc423 100644 --- a/arch/x86/mm/numa.c +++ b/arch/x86/mm/numa.c @@ -463,6 +463,42 @@ static bool __init numa_meminfo_cover_memory(const struct numa_meminfo *mi) return true; } +static void __init numa_clear_kernel_node_hotplug(void) +{ + int i, nid; + nodemask_t numa_kernel_nodes = NODE_MASK_NONE; + unsigned long start, end; + struct memblock_region *r; + + /* + * At this time, all memory regions reserved by memblock are + * used by the kernel. Set the nid in memblock.reserved will + * mark out all the nodes the kernel resides in. + */ + for (i = 0; i < numa_meminfo.nr_blks; i++) { + struct numa_memblk *mb = &numa_meminfo.blk[i]; + + memblock_set_node(mb->start, mb->end - mb->start, + &memblock.reserved, mb->nid); + } + + /* Mark all kernel nodes. */ + for_each_memblock(reserved, r) + node_set(r->nid, numa_kernel_nodes); + + /* Clear MEMBLOCK_HOTPLUG flag for memory in kernel nodes. */ + for (i = 0; i < numa_meminfo.nr_blks; i++) { + nid = numa_meminfo.blk[i].nid; + if (!node_isset(nid, numa_kernel_nodes)) + continue; + + start = numa_meminfo.blk[i].start; + end = numa_meminfo.blk[i].end; + + memblock_clear_hotplug(start, end - start); + } +} + static int __init numa_register_memblks(struct numa_meminfo *mi) { unsigned long uninitialized_var(pfn_align); @@ -480,6 +516,15 @@ static int __init numa_register_memblks(struct numa_meminfo *mi) &memblock.memory, mb->nid); } + /* + * At very early time, the kernel have to use some memory such as + * loading the kernel image. We cannot prevent this anyway. So any + * node the kernel resides in should be un-hotpluggable. + * + * And when we come here, alloc node data won't fail. + */ + numa_clear_kernel_node_hotplug(); + /* * If sections array is gonna be used for pfn -> nid mapping, check * whether its granularity is fine enough. @@ -548,41 +593,6 @@ static void __init numa_init_array(void) } } -static void __init numa_clear_kernel_node_hotplug(void) -{ - int i, nid; - nodemask_t numa_kernel_nodes = NODE_MASK_NONE; - unsigned long start, end; - struct memblock_region *r; - - /* - * At this time, all memory regions reserved by memblock are - * used by the kernel. Set the nid in memblock.reserved will - * mark out all the nodes the kernel resides in. - */ - for (i = 0; i < numa_meminfo.nr_blks; i++) { - struct numa_memblk *mb = &numa_meminfo.blk[i]; - memblock_set_node(mb->start, mb->end - mb->start, - &memblock.reserved, mb->nid); - } - - /* Mark all kernel nodes. */ - for_each_memblock(reserved, r) - node_set(r->nid, numa_kernel_nodes); - - /* Clear MEMBLOCK_HOTPLUG flag for memory in kernel nodes. */ - for (i = 0; i < numa_meminfo.nr_blks; i++) { - nid = numa_meminfo.blk[i].nid; - if (!node_isset(nid, numa_kernel_nodes)) - continue; - - start = numa_meminfo.blk[i].start; - end = numa_meminfo.blk[i].end; - - memblock_clear_hotplug(start, end - start); - } -} - static int __init numa_init(int (*init_func)(void)) { int i; @@ -637,15 +647,6 @@ static int __init numa_init(int (*init_func)(void)) } numa_init_array(); - /* - * At very early time, the kernel have to use some memory such as - * loading the kernel image. We cannot prevent this anyway. So any - * node the kernel resides in should be un-hotpluggable. - * - * And when we come here, numa_init() won't fail. - */ - numa_clear_kernel_node_hotplug(); - return 0; } diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index d56cd1f515bdb8037e67e0cbd45d1bd7b5c0db7f..3f627345d51cbd1a3dd453bb37cc82d7f75d5bb0 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -182,12 +182,17 @@ struct jit_context { bool seen_ld_abs; }; +/* maximum number of bytes emitted while JITing one eBPF insn */ +#define BPF_MAX_INSN_SIZE 128 +#define BPF_INSN_SAFETY 64 + static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, int oldproglen, struct jit_context *ctx) { struct bpf_insn *insn = bpf_prog->insnsi; int insn_cnt = bpf_prog->len; - u8 temp[64]; + bool seen_ld_abs = ctx->seen_ld_abs | (oldproglen == 0); + u8 temp[BPF_MAX_INSN_SIZE + BPF_INSN_SAFETY]; int i; int proglen = 0; u8 *prog = temp; @@ -225,7 +230,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, EMIT2(0x31, 0xc0); /* xor eax, eax */ EMIT3(0x4D, 0x31, 0xED); /* xor r13, r13 */ - if (ctx->seen_ld_abs) { + if (seen_ld_abs) { /* r9d : skb->len - skb->data_len (headlen) * r10 : skb->data */ @@ -685,7 +690,7 @@ xadd: if (is_imm8(insn->off)) case BPF_JMP | BPF_CALL: func = (u8 *) __bpf_call_base + imm32; jmp_offset = func - (image + addrs[i]); - if (ctx->seen_ld_abs) { + if (seen_ld_abs) { EMIT2(0x41, 0x52); /* push %r10 */ EMIT2(0x41, 0x51); /* push %r9 */ /* need to adjust jmp offset, since @@ -699,7 +704,7 @@ xadd: if (is_imm8(insn->off)) return -EINVAL; } EMIT1_off32(0xE8, jmp_offset); - if (ctx->seen_ld_abs) { + if (seen_ld_abs) { EMIT2(0x41, 0x59); /* pop %r9 */ EMIT2(0x41, 0x5A); /* pop %r10 */ } @@ -804,7 +809,8 @@ xadd: if (is_imm8(insn->off)) goto common_load; case BPF_LD | BPF_ABS | BPF_W: func = CHOOSE_LOAD_FUNC(imm32, sk_load_word); -common_load: ctx->seen_ld_abs = true; +common_load: + ctx->seen_ld_abs = seen_ld_abs = true; jmp_offset = func - (image + addrs[i]); if (!func || !is_simm32(jmp_offset)) { pr_err("unsupported bpf func %d addr %p image %p\n", @@ -878,6 +884,11 @@ common_load: ctx->seen_ld_abs = true; } ilen = prog - temp; + if (ilen > BPF_MAX_INSN_SIZE) { + pr_err("bpf_jit_compile fatal insn size error\n"); + return -EFAULT; + } + if (image) { if (unlikely(proglen + ilen > oldproglen)) { pr_err("bpf_jit_compile fatal error\n"); @@ -934,9 +945,11 @@ void bpf_int_jit_compile(struct bpf_prog *prog) goto out; } if (image) { - if (proglen != oldproglen) + if (proglen != oldproglen) { pr_err("bpf_jit: proglen=%d != oldproglen=%d\n", proglen, oldproglen); + goto out; + } break; } if (proglen == oldproglen) { diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c index 379e8bd0deeabf8bb1e839ea6bcb11f0bc1dd12b..1d2e6392f5faaf6f450cdfa598b9b2a734064962 100644 --- a/arch/x86/oprofile/nmi_int.c +++ b/arch/x86/oprofile/nmi_int.c @@ -64,11 +64,11 @@ u64 op_x86_get_ctrl(struct op_x86_model_spec const *model, static int profile_exceptions_notify(unsigned int val, struct pt_regs *regs) { if (ctr_running) - model->check_ctrs(regs, &__get_cpu_var(cpu_msrs)); + model->check_ctrs(regs, this_cpu_ptr(&cpu_msrs)); else if (!nmi_enabled) return NMI_DONE; else - model->stop(&__get_cpu_var(cpu_msrs)); + model->stop(this_cpu_ptr(&cpu_msrs)); return NMI_HANDLED; } @@ -91,7 +91,7 @@ static void nmi_cpu_save_registers(struct op_msrs *msrs) static void nmi_cpu_start(void *dummy) { - struct op_msrs const *msrs = &__get_cpu_var(cpu_msrs); + struct op_msrs const *msrs = this_cpu_ptr(&cpu_msrs); if (!msrs->controls) WARN_ON_ONCE(1); else @@ -111,7 +111,7 @@ static int nmi_start(void) static void nmi_cpu_stop(void *dummy) { - struct op_msrs const *msrs = &__get_cpu_var(cpu_msrs); + struct op_msrs const *msrs = this_cpu_ptr(&cpu_msrs); if (!msrs->controls) WARN_ON_ONCE(1); else diff --git a/arch/x86/oprofile/op_model_p4.c b/arch/x86/oprofile/op_model_p4.c index 98ab13058f892ee13048ac8962b13330f17e4d16..ad1d91f475ab009dbbd8e17320b7c001c82b5483 100644 --- a/arch/x86/oprofile/op_model_p4.c +++ b/arch/x86/oprofile/op_model_p4.c @@ -372,7 +372,7 @@ static unsigned int get_stagger(void) { #ifdef CONFIG_SMP int cpu = smp_processor_id(); - return cpu != cpumask_first(__get_cpu_var(cpu_sibling_map)); + return cpu != cpumask_first(this_cpu_cpumask_var_ptr(cpu_sibling_map)); #endif return 0; } diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c index c89c93320c12a4dfa21156e86937eea979af480d..c6b146e6711693bbde4d5e3694ac27755baae7f9 100644 --- a/arch/x86/platform/uv/uv_nmi.c +++ b/arch/x86/platform/uv/uv_nmi.c @@ -63,8 +63,8 @@ static struct uv_hub_nmi_s **uv_hub_nmi_list; -DEFINE_PER_CPU(struct uv_cpu_nmi_s, __uv_cpu_nmi); -EXPORT_PER_CPU_SYMBOL_GPL(__uv_cpu_nmi); +DEFINE_PER_CPU(struct uv_cpu_nmi_s, uv_cpu_nmi); +EXPORT_PER_CPU_SYMBOL_GPL(uv_cpu_nmi); static unsigned long nmi_mmr; static unsigned long nmi_mmr_clear; @@ -215,7 +215,7 @@ static int uv_check_nmi(struct uv_hub_nmi_s *hub_nmi) int nmi = 0; local64_inc(&uv_nmi_count); - uv_cpu_nmi.queries++; + this_cpu_inc(uv_cpu_nmi.queries); do { nmi = atomic_read(&hub_nmi->in_nmi); @@ -293,7 +293,7 @@ static void uv_nmi_nr_cpus_ping(void) int cpu; for_each_cpu(cpu, uv_nmi_cpu_mask) - atomic_set(&uv_cpu_nmi_per(cpu).pinging, 1); + uv_cpu_nmi_per(cpu).pinging = 1; apic->send_IPI_mask(uv_nmi_cpu_mask, APIC_DM_NMI); } @@ -304,8 +304,8 @@ static void uv_nmi_cleanup_mask(void) int cpu; for_each_cpu(cpu, uv_nmi_cpu_mask) { - atomic_set(&uv_cpu_nmi_per(cpu).pinging, 0); - atomic_set(&uv_cpu_nmi_per(cpu).state, UV_NMI_STATE_OUT); + uv_cpu_nmi_per(cpu).pinging = 0; + uv_cpu_nmi_per(cpu).state = UV_NMI_STATE_OUT; cpumask_clear_cpu(cpu, uv_nmi_cpu_mask); } } @@ -328,7 +328,7 @@ static int uv_nmi_wait_cpus(int first) int loop_delay = uv_nmi_loop_delay; for_each_cpu(j, uv_nmi_cpu_mask) { - if (atomic_read(&uv_cpu_nmi_per(j).state)) { + if (uv_cpu_nmi_per(j).state) { cpumask_clear_cpu(j, uv_nmi_cpu_mask); if (++k >= n) break; @@ -359,7 +359,7 @@ static int uv_nmi_wait_cpus(int first) static void uv_nmi_wait(int master) { /* indicate this cpu is in */ - atomic_set(&uv_cpu_nmi.state, UV_NMI_STATE_IN); + this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_IN); /* if not the first cpu in (the master), then we are a slave cpu */ if (!master) @@ -419,7 +419,7 @@ static void uv_nmi_dump_state_cpu(int cpu, struct pt_regs *regs) "UV:%sNMI process trace for CPU %d\n", dots, cpu); show_regs(regs); } - atomic_set(&uv_cpu_nmi.state, UV_NMI_STATE_DUMP_DONE); + this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_DUMP_DONE); } /* Trigger a slave cpu to dump it's state */ @@ -427,20 +427,20 @@ static void uv_nmi_trigger_dump(int cpu) { int retry = uv_nmi_trigger_delay; - if (atomic_read(&uv_cpu_nmi_per(cpu).state) != UV_NMI_STATE_IN) + if (uv_cpu_nmi_per(cpu).state != UV_NMI_STATE_IN) return; - atomic_set(&uv_cpu_nmi_per(cpu).state, UV_NMI_STATE_DUMP); + uv_cpu_nmi_per(cpu).state = UV_NMI_STATE_DUMP; do { cpu_relax(); udelay(10); - if (atomic_read(&uv_cpu_nmi_per(cpu).state) + if (uv_cpu_nmi_per(cpu).state != UV_NMI_STATE_DUMP) return; } while (--retry > 0); pr_crit("UV: CPU %d stuck in process dump function\n", cpu); - atomic_set(&uv_cpu_nmi_per(cpu).state, UV_NMI_STATE_DUMP_DONE); + uv_cpu_nmi_per(cpu).state = UV_NMI_STATE_DUMP_DONE; } /* Wait until all cpus ready to exit */ @@ -488,7 +488,7 @@ static void uv_nmi_dump_state(int cpu, struct pt_regs *regs, int master) } else { while (!atomic_read(&uv_nmi_slave_continue)) cpu_relax(); - while (atomic_read(&uv_cpu_nmi.state) != UV_NMI_STATE_DUMP) + while (this_cpu_read(uv_cpu_nmi.state) != UV_NMI_STATE_DUMP) cpu_relax(); uv_nmi_dump_state_cpu(cpu, regs); } @@ -615,7 +615,7 @@ int uv_handle_nmi(unsigned int reason, struct pt_regs *regs) local_irq_save(flags); /* If not a UV System NMI, ignore */ - if (!atomic_read(&uv_cpu_nmi.pinging) && !uv_check_nmi(hub_nmi)) { + if (!this_cpu_read(uv_cpu_nmi.pinging) && !uv_check_nmi(hub_nmi)) { local_irq_restore(flags); return NMI_DONE; } @@ -639,7 +639,7 @@ int uv_handle_nmi(unsigned int reason, struct pt_regs *regs) uv_call_kgdb_kdb(cpu, regs, master); /* Clear per_cpu "in nmi" flag */ - atomic_set(&uv_cpu_nmi.state, UV_NMI_STATE_OUT); + this_cpu_write(uv_cpu_nmi.state, UV_NMI_STATE_OUT); /* Clear MMR NMI flag on each hub */ uv_clear_nmi(cpu); @@ -666,16 +666,16 @@ static int uv_handle_nmi_ping(unsigned int reason, struct pt_regs *regs) { int ret; - uv_cpu_nmi.queries++; - if (!atomic_read(&uv_cpu_nmi.pinging)) { + this_cpu_inc(uv_cpu_nmi.queries); + if (!this_cpu_read(uv_cpu_nmi.pinging)) { local64_inc(&uv_nmi_ping_misses); return NMI_DONE; } - uv_cpu_nmi.pings++; + this_cpu_inc(uv_cpu_nmi.pings); local64_inc(&uv_nmi_ping_count); ret = uv_handle_nmi(reason, regs); - atomic_set(&uv_cpu_nmi.pinging, 0); + this_cpu_write(uv_cpu_nmi.pinging, 0); return ret; } diff --git a/arch/x86/platform/uv/uv_time.c b/arch/x86/platform/uv/uv_time.c index 5c86786bbfd2e363a60c88e78ea73f6fb22753eb..a244237f3cfa834af62fb81eabed244548a50f90 100644 --- a/arch/x86/platform/uv/uv_time.c +++ b/arch/x86/platform/uv/uv_time.c @@ -365,7 +365,7 @@ __setup("uvrtcevt", uv_enable_evt_rtc); static __init void uv_rtc_register_clockevents(struct work_struct *dummy) { - struct clock_event_device *ced = &__get_cpu_var(cpu_ced); + struct clock_event_device *ced = this_cpu_ptr(&cpu_ced); *ced = clock_event_device_uv; ced->cpumask = cpumask_of(smp_processor_id()); diff --git a/arch/x86/purgatory/Makefile b/arch/x86/purgatory/Makefile index 899dd24542568de85e815e4b9f206d66286bca8c..f52e033557c9e0b54c41248101e0d7d7ed7cb76f 100644 --- a/arch/x86/purgatory/Makefile +++ b/arch/x86/purgatory/Makefile @@ -18,8 +18,9 @@ $(obj)/purgatory.ro: $(PURGATORY_OBJS) FORCE targets += kexec-purgatory.c +CMD_BIN2C = $(objtree)/scripts/basic/bin2c quiet_cmd_bin2c = BIN2C $@ - cmd_bin2c = cat $(obj)/purgatory.ro | $(objtree)/scripts/basic/bin2c kexec_purgatory > $(obj)/kexec-purgatory.c + cmd_bin2c = $(CMD_BIN2C) kexec_purgatory < $< > $@ $(obj)/kexec-purgatory.c: $(obj)/purgatory.ro FORCE $(call if_changed,bin2c) diff --git a/arch/x86/um/asm/ptrace.h b/arch/x86/um/asm/ptrace.h index 54f8102ccde5d2774f375f6584c31c712f46d915..e59eef20647b3e722cc3f5a97fdec08c29071c14 100644 --- a/arch/x86/um/asm/ptrace.h +++ b/arch/x86/um/asm/ptrace.h @@ -47,8 +47,6 @@ struct user_desc; #ifdef CONFIG_X86_32 -#define HOST_AUDIT_ARCH AUDIT_ARCH_I386 - extern int ptrace_get_thread_area(struct task_struct *child, int idx, struct user_desc __user *user_desc); @@ -57,8 +55,6 @@ extern int ptrace_set_thread_area(struct task_struct *child, int idx, #else -#define HOST_AUDIT_ARCH AUDIT_ARCH_X86_64 - #define PT_REGS_R8(r) UPT_R8(&(r)->regs) #define PT_REGS_R9(r) UPT_R9(&(r)->regs) #define PT_REGS_R10(r) UPT_R10(&(r)->regs) diff --git a/arch/x86/um/asm/syscall.h b/arch/x86/um/asm/syscall.h new file mode 100644 index 0000000000000000000000000000000000000000..9fe77b7b5a0ed5b77f05174e8ea87f39cb80e2ed --- /dev/null +++ b/arch/x86/um/asm/syscall.h @@ -0,0 +1,15 @@ +#ifndef __UM_ASM_SYSCALL_H +#define __UM_ASM_SYSCALL_H + +#include + +static inline int syscall_get_arch(void) +{ +#ifdef CONFIG_X86_32 + return AUDIT_ARCH_I386; +#else + return AUDIT_ARCH_X86_64; +#endif +} + +#endif /* __UM_ASM_SYSCALL_H */ diff --git a/arch/x86/um/checksum_32.S b/arch/x86/um/checksum_32.S index 8d0c420465cce0cf897278e03f25904a51cd92ff..fa4b8b9841ff7071a993e61d2b55ca3bc8b35575 100644 --- a/arch/x86/um/checksum_32.S +++ b/arch/x86/um/checksum_32.S @@ -214,242 +214,3 @@ csum_partial: ret #endif - -/* -unsigned int csum_partial_copy_generic (const char *src, char *dst, - int len, int sum, int *src_err_ptr, int *dst_err_ptr) - */ - -/* - * Copy from ds while checksumming, otherwise like csum_partial - * - * The macros SRC and DST specify the type of access for the instruction. - * thus we can call a custom exception handler for all access types. - * - * FIXME: could someone double-check whether I haven't mixed up some SRC and - * DST definitions? It's damn hard to trigger all cases. I hope I got - * them all but there's no guarantee. - */ - -#define SRC(y...) \ - 9999: y; \ - _ASM_EXTABLE(9999b, 6001f) - -#define DST(y...) \ - 9999: y; \ - _ASM_EXTABLE(9999b, 6002f) - -.align 4 - -#ifndef CONFIG_X86_USE_PPRO_CHECKSUM - -#define ARGBASE 16 -#define FP 12 - -csum_partial_copy_generic_i386: - subl $4,%esp - pushl %edi - pushl %esi - pushl %ebx - movl ARGBASE+16(%esp),%eax # sum - movl ARGBASE+12(%esp),%ecx # len - movl ARGBASE+4(%esp),%esi # src - movl ARGBASE+8(%esp),%edi # dst - - testl $2, %edi # Check alignment. - jz 2f # Jump if alignment is ok. - subl $2, %ecx # Alignment uses up two bytes. - jae 1f # Jump if we had at least two bytes. - addl $2, %ecx # ecx was < 2. Deal with it. - jmp 4f -SRC(1: movw (%esi), %bx ) - addl $2, %esi -DST( movw %bx, (%edi) ) - addl $2, %edi - addw %bx, %ax - adcl $0, %eax -2: - movl %ecx, FP(%esp) - shrl $5, %ecx - jz 2f - testl %esi, %esi -SRC(1: movl (%esi), %ebx ) -SRC( movl 4(%esi), %edx ) - adcl %ebx, %eax -DST( movl %ebx, (%edi) ) - adcl %edx, %eax -DST( movl %edx, 4(%edi) ) - -SRC( movl 8(%esi), %ebx ) -SRC( movl 12(%esi), %edx ) - adcl %ebx, %eax -DST( movl %ebx, 8(%edi) ) - adcl %edx, %eax -DST( movl %edx, 12(%edi) ) - -SRC( movl 16(%esi), %ebx ) -SRC( movl 20(%esi), %edx ) - adcl %ebx, %eax -DST( movl %ebx, 16(%edi) ) - adcl %edx, %eax -DST( movl %edx, 20(%edi) ) - -SRC( movl 24(%esi), %ebx ) -SRC( movl 28(%esi), %edx ) - adcl %ebx, %eax -DST( movl %ebx, 24(%edi) ) - adcl %edx, %eax -DST( movl %edx, 28(%edi) ) - - lea 32(%esi), %esi - lea 32(%edi), %edi - dec %ecx - jne 1b - adcl $0, %eax -2: movl FP(%esp), %edx - movl %edx, %ecx - andl $0x1c, %edx - je 4f - shrl $2, %edx # This clears CF -SRC(3: movl (%esi), %ebx ) - adcl %ebx, %eax -DST( movl %ebx, (%edi) ) - lea 4(%esi), %esi - lea 4(%edi), %edi - dec %edx - jne 3b - adcl $0, %eax -4: andl $3, %ecx - jz 7f - cmpl $2, %ecx - jb 5f -SRC( movw (%esi), %cx ) - leal 2(%esi), %esi -DST( movw %cx, (%edi) ) - leal 2(%edi), %edi - je 6f - shll $16,%ecx -SRC(5: movb (%esi), %cl ) -DST( movb %cl, (%edi) ) -6: addl %ecx, %eax - adcl $0, %eax -7: -5000: - -# Exception handler: -.section .fixup, "ax" - -6001: - movl ARGBASE+20(%esp), %ebx # src_err_ptr - movl $-EFAULT, (%ebx) - - # zero the complete destination - computing the rest - # is too much work - movl ARGBASE+8(%esp), %edi # dst - movl ARGBASE+12(%esp), %ecx # len - xorl %eax,%eax - rep ; stosb - - jmp 5000b - -6002: - movl ARGBASE+24(%esp), %ebx # dst_err_ptr - movl $-EFAULT,(%ebx) - jmp 5000b - -.previous - - popl %ebx - popl %esi - popl %edi - popl %ecx # equivalent to addl $4,%esp - ret - -#else - -/* Version for PentiumII/PPro */ - -#define ROUND1(x) \ - SRC(movl x(%esi), %ebx ) ; \ - addl %ebx, %eax ; \ - DST(movl %ebx, x(%edi) ) ; - -#define ROUND(x) \ - SRC(movl x(%esi), %ebx ) ; \ - adcl %ebx, %eax ; \ - DST(movl %ebx, x(%edi) ) ; - -#define ARGBASE 12 - -csum_partial_copy_generic_i386: - pushl %ebx - pushl %edi - pushl %esi - movl ARGBASE+4(%esp),%esi #src - movl ARGBASE+8(%esp),%edi #dst - movl ARGBASE+12(%esp),%ecx #len - movl ARGBASE+16(%esp),%eax #sum -# movl %ecx, %edx - movl %ecx, %ebx - movl %esi, %edx - shrl $6, %ecx - andl $0x3c, %ebx - negl %ebx - subl %ebx, %esi - subl %ebx, %edi - lea -1(%esi),%edx - andl $-32,%edx - lea 3f(%ebx,%ebx), %ebx - testl %esi, %esi - jmp *%ebx -1: addl $64,%esi - addl $64,%edi - SRC(movb -32(%edx),%bl) ; SRC(movb (%edx),%bl) - ROUND1(-64) ROUND(-60) ROUND(-56) ROUND(-52) - ROUND (-48) ROUND(-44) ROUND(-40) ROUND(-36) - ROUND (-32) ROUND(-28) ROUND(-24) ROUND(-20) - ROUND (-16) ROUND(-12) ROUND(-8) ROUND(-4) -3: adcl $0,%eax - addl $64, %edx - dec %ecx - jge 1b -4: movl ARGBASE+12(%esp),%edx #len - andl $3, %edx - jz 7f - cmpl $2, %edx - jb 5f -SRC( movw (%esi), %dx ) - leal 2(%esi), %esi -DST( movw %dx, (%edi) ) - leal 2(%edi), %edi - je 6f - shll $16,%edx -5: -SRC( movb (%esi), %dl ) -DST( movb %dl, (%edi) ) -6: addl %edx, %eax - adcl $0, %eax -7: -.section .fixup, "ax" -6001: movl ARGBASE+20(%esp), %ebx # src_err_ptr - movl $-EFAULT, (%ebx) - # zero the complete destination (computing the rest is too much work) - movl ARGBASE+8(%esp),%edi # dst - movl ARGBASE+12(%esp),%ecx # len - xorl %eax,%eax - rep; stosb - jmp 7b -6002: movl ARGBASE+24(%esp), %ebx # dst_err_ptr - movl $-EFAULT, (%ebx) - jmp 7b -.previous - - popl %esi - popl %edi - popl %ebx - ret - -#undef ROUND -#undef ROUND1 - -#endif diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index acb0effd80773f347ef8a7e6591d6a447c894423..1a3f0445432a31fa1b654ab8af882bc317e5d363 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -821,7 +821,7 @@ static void xen_convert_trap_info(const struct desc_ptr *desc, void xen_copy_trap_info(struct trap_info *traps) { - const struct desc_ptr *desc = &__get_cpu_var(idt_desc); + const struct desc_ptr *desc = this_cpu_ptr(&idt_desc); xen_convert_trap_info(desc, traps); } @@ -838,7 +838,7 @@ static void xen_load_idt(const struct desc_ptr *desc) spin_lock(&lock); - __get_cpu_var(idt_desc) = *desc; + memcpy(this_cpu_ptr(&idt_desc), desc, sizeof(idt_desc)); xen_convert_trap_info(desc, traps); diff --git a/arch/x86/xen/multicalls.c b/arch/x86/xen/multicalls.c index 0d82003e76adab8afe2ef3848f0c4384cd99601e..ea54a08d83012dce01f542e46cbaf453a0881ba5 100644 --- a/arch/x86/xen/multicalls.c +++ b/arch/x86/xen/multicalls.c @@ -54,7 +54,7 @@ DEFINE_PER_CPU(unsigned long, xen_mc_irq_flags); void xen_mc_flush(void) { - struct mc_buffer *b = &__get_cpu_var(mc_buffer); + struct mc_buffer *b = this_cpu_ptr(&mc_buffer); struct multicall_entry *mc; int ret = 0; unsigned long flags; @@ -131,7 +131,7 @@ void xen_mc_flush(void) struct multicall_space __xen_mc_entry(size_t args) { - struct mc_buffer *b = &__get_cpu_var(mc_buffer); + struct mc_buffer *b = this_cpu_ptr(&mc_buffer); struct multicall_space ret; unsigned argidx = roundup(b->argidx, sizeof(u64)); @@ -162,7 +162,7 @@ struct multicall_space __xen_mc_entry(size_t args) struct multicall_space xen_mc_extend_args(unsigned long op, size_t size) { - struct mc_buffer *b = &__get_cpu_var(mc_buffer); + struct mc_buffer *b = this_cpu_ptr(&mc_buffer); struct multicall_space ret = { NULL, NULL }; BUG_ON(preemptible()); @@ -192,7 +192,7 @@ struct multicall_space xen_mc_extend_args(unsigned long op, size_t size) void xen_mc_callback(void (*fn)(void *), void *data) { - struct mc_buffer *b = &__get_cpu_var(mc_buffer); + struct mc_buffer *b = this_cpu_ptr(&mc_buffer); struct callback *cb; if (b->cbidx == MC_BATCH) { diff --git a/arch/x86/xen/spinlock.c b/arch/x86/xen/spinlock.c index 0ba5f3b967f0041c5ea6445f898a944008b96c58..23b45eb9a89ce4d56f8a73e181d004b107dc1f81 100644 --- a/arch/x86/xen/spinlock.c +++ b/arch/x86/xen/spinlock.c @@ -109,7 +109,7 @@ static bool xen_pvspin = true; __visible void xen_lock_spinning(struct arch_spinlock *lock, __ticket_t want) { int irq = __this_cpu_read(lock_kicker_irq); - struct xen_lock_waiting *w = &__get_cpu_var(lock_waiting); + struct xen_lock_waiting *w = this_cpu_ptr(&lock_waiting); int cpu = smp_processor_id(); u64 start; unsigned long flags; diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index 5718b0b58b60f663a845207af02d930962e96b8f..a1d430b112b33cc23ad07f925fa96325d327127f 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -80,7 +80,7 @@ static void get_runstate_snapshot(struct vcpu_runstate_info *res) BUG_ON(preemptible()); - state = &__get_cpu_var(xen_runstate); + state = this_cpu_ptr(&xen_runstate); /* * The runstate info is always updated by the hypervisor on @@ -123,7 +123,7 @@ static void do_stolen_accounting(void) WARN_ON(state.state != RUNSTATE_running); - snap = &__get_cpu_var(xen_runstate_snapshot); + snap = this_cpu_ptr(&xen_runstate_snapshot); /* work out how much time the VCPU has not been runn*ing* */ runnable = state.time[RUNSTATE_runnable] - snap->time[RUNSTATE_runnable]; @@ -158,7 +158,7 @@ cycle_t xen_clocksource_read(void) cycle_t ret; preempt_disable_notrace(); - src = &__get_cpu_var(xen_vcpu)->time; + src = this_cpu_ptr(&xen_vcpu->time); ret = pvclock_clocksource_read(src); preempt_enable_notrace(); return ret; @@ -397,7 +397,7 @@ static DEFINE_PER_CPU(struct xen_clock_event_device, xen_clock_events) = { .evt. static irqreturn_t xen_timer_interrupt(int irq, void *dev_id) { - struct clock_event_device *evt = &__get_cpu_var(xen_clock_events).evt; + struct clock_event_device *evt = this_cpu_ptr(&xen_clock_events.evt); irqreturn_t ret; ret = IRQ_NONE; @@ -460,7 +460,7 @@ void xen_setup_cpu_clockevents(void) { BUG_ON(preemptible()); - clockevents_register_device(&__get_cpu_var(xen_clock_events).evt); + clockevents_register_device(this_cpu_ptr(&xen_clock_events.evt)); } void xen_timer_resume(void) diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c index 562fac664751df705f2c31cb3a2bc245e9ebd9ec..4d54b481123b64e286f45f6fc03121a0d5024474 100644 --- a/arch/xtensa/kernel/ptrace.c +++ b/arch/xtensa/kernel/ptrace.c @@ -342,7 +342,7 @@ void do_syscall_trace_enter(struct pt_regs *regs) do_syscall_trace(); #if 0 - audit_syscall_entry(current, AUDIT_ARCH_XTENSA..); + audit_syscall_entry(...); #endif } diff --git a/block/Kconfig b/block/Kconfig index 2429515c05c2ed1b6f457a60def817c33c178651..161491d0a879ed11ce2276a1923a60bc1d92479e 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -77,6 +77,7 @@ config BLK_DEV_BSGLIB config BLK_DEV_INTEGRITY bool "Block layer data integrity support" + select CRC_T10DIF if BLK_DEV_INTEGRITY ---help--- Some storage devices allow extra information to be stored/retrieved to help protect the data. The block layer diff --git a/block/Makefile b/block/Makefile index a2ce6ac935ecf566ec8e956f666f76feec4e9682..00ecc97629db783d735798c961579ee1a531c367 100644 --- a/block/Makefile +++ b/block/Makefile @@ -20,6 +20,6 @@ obj-$(CONFIG_IOSCHED_DEADLINE) += deadline-iosched.o obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o -obj-$(CONFIG_BLK_DEV_INTEGRITY) += blk-integrity.o obj-$(CONFIG_BLK_CMDLINE_PARSER) += cmdline-parser.o -obj-$(CONFIG_BLK_DEV_INTEGRITY) += bio-integrity.o +obj-$(CONFIG_BLK_DEV_INTEGRITY) += bio-integrity.o blk-integrity.o t10-pi.o + diff --git a/block/bio-integrity.c b/block/bio-integrity.c index f14b4abbebd89a0b812a67b06e3a9bb2dffb707b..0984232e429fb61d427c90340fa0c77e55c1fee2 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -79,6 +79,7 @@ struct bio_integrity_payload *bio_integrity_alloc(struct bio *bio, bip->bip_slab = idx; bip->bip_bio = bio; bio->bi_integrity = bip; + bio->bi_rw |= REQ_INTEGRITY; return bip; err: @@ -96,11 +97,12 @@ EXPORT_SYMBOL(bio_integrity_alloc); */ void bio_integrity_free(struct bio *bio) { - struct bio_integrity_payload *bip = bio->bi_integrity; + struct bio_integrity_payload *bip = bio_integrity(bio); struct bio_set *bs = bio->bi_pool; - if (bip->bip_owns_buf) - kfree(bip->bip_buf); + if (bip->bip_flags & BIP_BLOCK_INTEGRITY) + kfree(page_address(bip->bip_vec->bv_page) + + bip->bip_vec->bv_offset); if (bs) { if (bip->bip_slab != BIO_POOL_NONE) @@ -128,7 +130,7 @@ EXPORT_SYMBOL(bio_integrity_free); int bio_integrity_add_page(struct bio *bio, struct page *page, unsigned int len, unsigned int offset) { - struct bio_integrity_payload *bip = bio->bi_integrity; + struct bio_integrity_payload *bip = bio_integrity(bio); struct bio_vec *iv; if (bip->bip_vcnt >= bip->bip_max_vcnt) { @@ -147,24 +149,6 @@ int bio_integrity_add_page(struct bio *bio, struct page *page, } EXPORT_SYMBOL(bio_integrity_add_page); -static int bdev_integrity_enabled(struct block_device *bdev, int rw) -{ - struct blk_integrity *bi = bdev_get_integrity(bdev); - - if (bi == NULL) - return 0; - - if (rw == READ && bi->verify_fn != NULL && - (bi->flags & INTEGRITY_FLAG_READ)) - return 1; - - if (rw == WRITE && bi->generate_fn != NULL && - (bi->flags & INTEGRITY_FLAG_WRITE)) - return 1; - - return 0; -} - /** * bio_integrity_enabled - Check whether integrity can be passed * @bio: bio to check @@ -174,199 +158,92 @@ static int bdev_integrity_enabled(struct block_device *bdev, int rw) * set prior to calling. The functions honors the write_generate and * read_verify flags in sysfs. */ -int bio_integrity_enabled(struct bio *bio) +bool bio_integrity_enabled(struct bio *bio) { + struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); + if (!bio_is_rw(bio)) - return 0; + return false; /* Already protected? */ if (bio_integrity(bio)) - return 0; + return false; + + if (bi == NULL) + return false; + + if (bio_data_dir(bio) == READ && bi->verify_fn != NULL && + (bi->flags & BLK_INTEGRITY_VERIFY)) + return true; + + if (bio_data_dir(bio) == WRITE && bi->generate_fn != NULL && + (bi->flags & BLK_INTEGRITY_GENERATE)) + return true; - return bdev_integrity_enabled(bio->bi_bdev, bio_data_dir(bio)); + return false; } EXPORT_SYMBOL(bio_integrity_enabled); /** - * bio_integrity_hw_sectors - Convert 512b sectors to hardware ditto + * bio_integrity_intervals - Return number of integrity intervals for a bio * @bi: blk_integrity profile for device - * @sectors: Number of 512 sectors to convert + * @sectors: Size of the bio in 512-byte sectors * * Description: The block layer calculates everything in 512 byte - * sectors but integrity metadata is done in terms of the hardware - * sector size of the storage device. Convert the block layer sectors - * to physical sectors. + * sectors but integrity metadata is done in terms of the data integrity + * interval size of the storage device. Convert the block layer sectors + * to the appropriate number of integrity intervals. */ -static inline unsigned int bio_integrity_hw_sectors(struct blk_integrity *bi, - unsigned int sectors) +static inline unsigned int bio_integrity_intervals(struct blk_integrity *bi, + unsigned int sectors) { - /* At this point there are only 512b or 4096b DIF/EPP devices */ - if (bi->sector_size == 4096) - return sectors >>= 3; - - return sectors; + return sectors >> (ilog2(bi->interval) - 9); } static inline unsigned int bio_integrity_bytes(struct blk_integrity *bi, unsigned int sectors) { - return bio_integrity_hw_sectors(bi, sectors) * bi->tuple_size; + return bio_integrity_intervals(bi, sectors) * bi->tuple_size; } /** - * bio_integrity_tag_size - Retrieve integrity tag space - * @bio: bio to inspect - * - * Description: Returns the maximum number of tag bytes that can be - * attached to this bio. Filesystems can use this to determine how - * much metadata to attach to an I/O. - */ -unsigned int bio_integrity_tag_size(struct bio *bio) -{ - struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); - - BUG_ON(bio->bi_iter.bi_size == 0); - - return bi->tag_size * (bio->bi_iter.bi_size / bi->sector_size); -} -EXPORT_SYMBOL(bio_integrity_tag_size); - -static int bio_integrity_tag(struct bio *bio, void *tag_buf, unsigned int len, - int set) -{ - struct bio_integrity_payload *bip = bio->bi_integrity; - struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); - unsigned int nr_sectors; - - BUG_ON(bip->bip_buf == NULL); - - if (bi->tag_size == 0) - return -1; - - nr_sectors = bio_integrity_hw_sectors(bi, - DIV_ROUND_UP(len, bi->tag_size)); - - if (nr_sectors * bi->tuple_size > bip->bip_iter.bi_size) { - printk(KERN_ERR "%s: tag too big for bio: %u > %u\n", __func__, - nr_sectors * bi->tuple_size, bip->bip_iter.bi_size); - return -1; - } - - if (set) - bi->set_tag_fn(bip->bip_buf, tag_buf, nr_sectors); - else - bi->get_tag_fn(bip->bip_buf, tag_buf, nr_sectors); - - return 0; -} - -/** - * bio_integrity_set_tag - Attach a tag buffer to a bio - * @bio: bio to attach buffer to - * @tag_buf: Pointer to a buffer containing tag data - * @len: Length of the included buffer - * - * Description: Use this function to tag a bio by leveraging the extra - * space provided by devices formatted with integrity protection. The - * size of the integrity buffer must be <= to the size reported by - * bio_integrity_tag_size(). - */ -int bio_integrity_set_tag(struct bio *bio, void *tag_buf, unsigned int len) -{ - BUG_ON(bio_data_dir(bio) != WRITE); - - return bio_integrity_tag(bio, tag_buf, len, 1); -} -EXPORT_SYMBOL(bio_integrity_set_tag); - -/** - * bio_integrity_get_tag - Retrieve a tag buffer from a bio - * @bio: bio to retrieve buffer from - * @tag_buf: Pointer to a buffer for the tag data - * @len: Length of the target buffer - * - * Description: Use this function to retrieve the tag buffer from a - * completed I/O. The size of the integrity buffer must be <= to the - * size reported by bio_integrity_tag_size(). - */ -int bio_integrity_get_tag(struct bio *bio, void *tag_buf, unsigned int len) -{ - BUG_ON(bio_data_dir(bio) != READ); - - return bio_integrity_tag(bio, tag_buf, len, 0); -} -EXPORT_SYMBOL(bio_integrity_get_tag); - -/** - * bio_integrity_generate_verify - Generate/verify integrity metadata for a bio + * bio_integrity_process - Process integrity metadata for a bio * @bio: bio to generate/verify integrity metadata for - * @operate: operate number, 1 for generate, 0 for verify + * @proc_fn: Pointer to the relevant processing function */ -static int bio_integrity_generate_verify(struct bio *bio, int operate) +static int bio_integrity_process(struct bio *bio, + integrity_processing_fn *proc_fn) { struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); - struct blk_integrity_exchg bix; + struct blk_integrity_iter iter; struct bio_vec *bv; - sector_t sector; - unsigned int sectors, ret = 0, i; - void *prot_buf = bio->bi_integrity->bip_buf; - - if (operate) - sector = bio->bi_iter.bi_sector; - else - sector = bio->bi_integrity->bip_iter.bi_sector; + struct bio_integrity_payload *bip = bio_integrity(bio); + unsigned int i, ret = 0; + void *prot_buf = page_address(bip->bip_vec->bv_page) + + bip->bip_vec->bv_offset; - bix.disk_name = bio->bi_bdev->bd_disk->disk_name; - bix.sector_size = bi->sector_size; + iter.disk_name = bio->bi_bdev->bd_disk->disk_name; + iter.interval = bi->interval; + iter.seed = bip_get_seed(bip); + iter.prot_buf = prot_buf; bio_for_each_segment_all(bv, bio, i) { void *kaddr = kmap_atomic(bv->bv_page); - bix.data_buf = kaddr + bv->bv_offset; - bix.data_size = bv->bv_len; - bix.prot_buf = prot_buf; - bix.sector = sector; - - if (operate) - bi->generate_fn(&bix); - else { - ret = bi->verify_fn(&bix); - if (ret) { - kunmap_atomic(kaddr); - return ret; - } - } - sectors = bv->bv_len / bi->sector_size; - sector += sectors; - prot_buf += sectors * bi->tuple_size; + iter.data_buf = kaddr + bv->bv_offset; + iter.data_size = bv->bv_len; + + ret = proc_fn(&iter); + if (ret) { + kunmap_atomic(kaddr); + return ret; + } kunmap_atomic(kaddr); } return ret; } -/** - * bio_integrity_generate - Generate integrity metadata for a bio - * @bio: bio to generate integrity metadata for - * - * Description: Generates integrity metadata for a bio by calling the - * block device's generation callback function. The bio must have a - * bip attached with enough room to accommodate the generated - * integrity metadata. - */ -static void bio_integrity_generate(struct bio *bio) -{ - bio_integrity_generate_verify(bio, 1); -} - -static inline unsigned short blk_integrity_tuple_size(struct blk_integrity *bi) -{ - if (bi) - return bi->tuple_size; - - return 0; -} - /** * bio_integrity_prep - Prepare bio for integrity I/O * @bio: bio to prepare @@ -387,17 +264,17 @@ int bio_integrity_prep(struct bio *bio) unsigned long start, end; unsigned int len, nr_pages; unsigned int bytes, offset, i; - unsigned int sectors; + unsigned int intervals; bi = bdev_get_integrity(bio->bi_bdev); q = bdev_get_queue(bio->bi_bdev); BUG_ON(bi == NULL); BUG_ON(bio_integrity(bio)); - sectors = bio_integrity_hw_sectors(bi, bio_sectors(bio)); + intervals = bio_integrity_intervals(bi, bio_sectors(bio)); /* Allocate kernel buffer for protection data */ - len = sectors * blk_integrity_tuple_size(bi); + len = intervals * bi->tuple_size; buf = kmalloc(len, GFP_NOIO | q->bounce_gfp); if (unlikely(buf == NULL)) { printk(KERN_ERR "could not allocate integrity buffer\n"); @@ -416,10 +293,12 @@ int bio_integrity_prep(struct bio *bio) return -EIO; } - bip->bip_owns_buf = 1; - bip->bip_buf = buf; + bip->bip_flags |= BIP_BLOCK_INTEGRITY; bip->bip_iter.bi_size = len; - bip->bip_iter.bi_sector = bio->bi_iter.bi_sector; + bip_set_seed(bip, bio->bi_iter.bi_sector); + + if (bi->flags & BLK_INTEGRITY_IP_CHECKSUM) + bip->bip_flags |= BIP_IP_CHECKSUM; /* Map it */ offset = offset_in_page(buf); @@ -455,25 +334,12 @@ int bio_integrity_prep(struct bio *bio) /* Auto-generate integrity metadata if this is a write */ if (bio_data_dir(bio) == WRITE) - bio_integrity_generate(bio); + bio_integrity_process(bio, bi->generate_fn); return 0; } EXPORT_SYMBOL(bio_integrity_prep); -/** - * bio_integrity_verify - Verify integrity metadata for a bio - * @bio: bio to verify - * - * Description: This function is called to verify the integrity of a - * bio. The data in the bio io_vec is compared to the integrity - * metadata returned by the HBA. - */ -static int bio_integrity_verify(struct bio *bio) -{ - return bio_integrity_generate_verify(bio, 0); -} - /** * bio_integrity_verify_fn - Integrity I/O completion worker * @work: Work struct stored in bio to be verified @@ -487,9 +353,10 @@ static void bio_integrity_verify_fn(struct work_struct *work) struct bio_integrity_payload *bip = container_of(work, struct bio_integrity_payload, bip_work); struct bio *bio = bip->bip_bio; + struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); int error; - error = bio_integrity_verify(bio); + error = bio_integrity_process(bio, bi->verify_fn); /* Restore original bio completion handler */ bio->bi_end_io = bip->bip_end_io; @@ -510,7 +377,7 @@ static void bio_integrity_verify_fn(struct work_struct *work) */ void bio_integrity_endio(struct bio *bio, int error) { - struct bio_integrity_payload *bip = bio->bi_integrity; + struct bio_integrity_payload *bip = bio_integrity(bio); BUG_ON(bip->bip_bio != bio); @@ -541,7 +408,7 @@ EXPORT_SYMBOL(bio_integrity_endio); */ void bio_integrity_advance(struct bio *bio, unsigned int bytes_done) { - struct bio_integrity_payload *bip = bio->bi_integrity; + struct bio_integrity_payload *bip = bio_integrity(bio); struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); unsigned bytes = bio_integrity_bytes(bi, bytes_done >> 9); @@ -563,7 +430,7 @@ EXPORT_SYMBOL(bio_integrity_advance); void bio_integrity_trim(struct bio *bio, unsigned int offset, unsigned int sectors) { - struct bio_integrity_payload *bip = bio->bi_integrity; + struct bio_integrity_payload *bip = bio_integrity(bio); struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); bio_integrity_advance(bio, offset << 9); @@ -582,7 +449,7 @@ EXPORT_SYMBOL(bio_integrity_trim); int bio_integrity_clone(struct bio *bio, struct bio *bio_src, gfp_t gfp_mask) { - struct bio_integrity_payload *bip_src = bio_src->bi_integrity; + struct bio_integrity_payload *bip_src = bio_integrity(bio_src); struct bio_integrity_payload *bip; BUG_ON(bip_src == NULL); @@ -646,6 +513,4 @@ void __init bio_integrity_init(void) sizeof(struct bio_integrity_payload) + sizeof(struct bio_vec) * BIP_INLINE_VECS, 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); - if (!bip_slab) - panic("Failed to create slab\n"); } diff --git a/block/bio.c b/block/bio.c index 3e6331d25d90c6aa507acd43bb90f2001c9e6641..3e6e1986a5b2c0c217532b816dc911cd544e3e11 100644 --- a/block/bio.c +++ b/block/bio.c @@ -428,6 +428,9 @@ struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs) front_pad = 0; inline_vecs = nr_iovecs; } else { + /* should not use nobvec bioset for nr_iovecs > 0 */ + if (WARN_ON_ONCE(!bs->bvec_pool && nr_iovecs > 0)) + return NULL; /* * generic_make_request() converts recursion to iteration; this * means if we're running beneath it, any bios we allocate and @@ -1900,20 +1903,9 @@ void bioset_free(struct bio_set *bs) } EXPORT_SYMBOL(bioset_free); -/** - * bioset_create - Create a bio_set - * @pool_size: Number of bio and bio_vecs to cache in the mempool - * @front_pad: Number of bytes to allocate in front of the returned bio - * - * Description: - * Set up a bio_set to be used with @bio_alloc_bioset. Allows the caller - * to ask for a number of bytes to be allocated in front of the bio. - * Front pad allocation is useful for embedding the bio inside - * another structure, to avoid allocating extra data to go with the bio. - * Note that the bio must be embedded at the END of that structure always, - * or things will break badly. - */ -struct bio_set *bioset_create(unsigned int pool_size, unsigned int front_pad) +static struct bio_set *__bioset_create(unsigned int pool_size, + unsigned int front_pad, + bool create_bvec_pool) { unsigned int back_pad = BIO_INLINE_VECS * sizeof(struct bio_vec); struct bio_set *bs; @@ -1938,9 +1930,11 @@ struct bio_set *bioset_create(unsigned int pool_size, unsigned int front_pad) if (!bs->bio_pool) goto bad; - bs->bvec_pool = biovec_create_pool(pool_size); - if (!bs->bvec_pool) - goto bad; + if (create_bvec_pool) { + bs->bvec_pool = biovec_create_pool(pool_size); + if (!bs->bvec_pool) + goto bad; + } bs->rescue_workqueue = alloc_workqueue("bioset", WQ_MEM_RECLAIM, 0); if (!bs->rescue_workqueue) @@ -1951,8 +1945,41 @@ struct bio_set *bioset_create(unsigned int pool_size, unsigned int front_pad) bioset_free(bs); return NULL; } + +/** + * bioset_create - Create a bio_set + * @pool_size: Number of bio and bio_vecs to cache in the mempool + * @front_pad: Number of bytes to allocate in front of the returned bio + * + * Description: + * Set up a bio_set to be used with @bio_alloc_bioset. Allows the caller + * to ask for a number of bytes to be allocated in front of the bio. + * Front pad allocation is useful for embedding the bio inside + * another structure, to avoid allocating extra data to go with the bio. + * Note that the bio must be embedded at the END of that structure always, + * or things will break badly. + */ +struct bio_set *bioset_create(unsigned int pool_size, unsigned int front_pad) +{ + return __bioset_create(pool_size, front_pad, true); +} EXPORT_SYMBOL(bioset_create); +/** + * bioset_create_nobvec - Create a bio_set without bio_vec mempool + * @pool_size: Number of bio to cache in the mempool + * @front_pad: Number of bytes to allocate in front of the returned bio + * + * Description: + * Same functionality as bioset_create() except that mempool is not + * created for bio_vecs. Saving some memory for bio_clone_fast() users. + */ +struct bio_set *bioset_create_nobvec(unsigned int pool_size, unsigned int front_pad) +{ + return __bioset_create(pool_size, front_pad, false); +} +EXPORT_SYMBOL(bioset_create_nobvec); + #ifdef CONFIG_BLK_CGROUP /** * bio_associate_current - associate a bio with %current diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index e17da947f6bd5d3a988e8acaa7cecf685ae21218..0ac817b750dbc7a15f0c69c21ed184d5882e1696 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -822,7 +822,6 @@ static void blkcg_css_free(struct cgroup_subsys_state *css) static struct cgroup_subsys_state * blkcg_css_alloc(struct cgroup_subsys_state *parent_css) { - static atomic64_t id_seq = ATOMIC64_INIT(0); struct blkcg *blkcg; if (!parent_css) { @@ -836,7 +835,6 @@ blkcg_css_alloc(struct cgroup_subsys_state *parent_css) blkcg->cfq_weight = CFQ_WEIGHT_DEFAULT; blkcg->cfq_leaf_weight = CFQ_WEIGHT_DEFAULT; - blkcg->id = atomic64_inc_return(&id_seq); /* root is 0, start from 1 */ done: spin_lock_init(&blkcg->lock); INIT_RADIX_TREE(&blkcg->blkg_tree, GFP_ATOMIC); diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index d3fd7aa3d2a369f9ef0017ece713965c95529e82..c567865b5f1df6baea2cdabf89663994a67e8ade 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -50,9 +50,6 @@ struct blkcg { struct blkcg_gq *blkg_hint; struct hlist_head blkg_list; - /* for policies to test whether associated blkcg has changed */ - uint64_t id; - /* TODO: per-policy storage in blkcg */ unsigned int cfq_weight; /* belongs to cfq */ unsigned int cfq_leaf_weight; diff --git a/block/blk-core.c b/block/blk-core.c index 9c888bd22b007c01d6b704115c4cf8b9a5a43833..0421b53e6431fe9c2522f0b280058594bc9608cb 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -83,18 +83,14 @@ void blk_queue_congestion_threshold(struct request_queue *q) * @bdev: device * * Locates the passed device's request queue and returns the address of its - * backing_dev_info - * - * Will return NULL if the request queue cannot be located. + * backing_dev_info. This function can only be called if @bdev is opened + * and the return value is never NULL. */ struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev) { - struct backing_dev_info *ret = NULL; struct request_queue *q = bdev_get_queue(bdev); - if (q) - ret = &q->backing_dev_info; - return ret; + return &q->backing_dev_info; } EXPORT_SYMBOL(blk_get_backing_dev_info); @@ -394,11 +390,13 @@ static void __blk_drain_queue(struct request_queue *q, bool drain_all) * be drained. Check all the queues and counters. */ if (drain_all) { + struct blk_flush_queue *fq = blk_get_flush_queue(q, NULL); drain |= !list_empty(&q->queue_head); for (i = 0; i < 2; i++) { drain |= q->nr_rqs[i]; drain |= q->in_flight[i]; - drain |= !list_empty(&q->flush_queue[i]); + if (fq) + drain |= !list_empty(&fq->flush_queue[i]); } } @@ -604,9 +602,6 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id) #ifdef CONFIG_BLK_CGROUP INIT_LIST_HEAD(&q->blkg_list); #endif - INIT_LIST_HEAD(&q->flush_queue[0]); - INIT_LIST_HEAD(&q->flush_queue[1]); - INIT_LIST_HEAD(&q->flush_data_in_flight); INIT_DELAYED_WORK(&q->delay_work, blk_delay_work); kobject_init(&q->kobj, &blk_queue_ktype); @@ -709,8 +704,8 @@ blk_init_allocated_queue(struct request_queue *q, request_fn_proc *rfn, if (!q) return NULL; - q->flush_rq = kzalloc(sizeof(struct request), GFP_KERNEL); - if (!q->flush_rq) + q->fq = blk_alloc_flush_queue(q, NUMA_NO_NODE, 0); + if (!q->fq) return NULL; if (blk_init_rl(&q->root_rl, q, GFP_KERNEL)) @@ -746,7 +741,7 @@ blk_init_allocated_queue(struct request_queue *q, request_fn_proc *rfn, return q; fail: - kfree(q->flush_rq); + blk_free_flush_queue(q->fq); return NULL; } EXPORT_SYMBOL(blk_init_allocated_queue); @@ -934,8 +929,8 @@ static struct io_context *rq_ioc(struct bio *bio) * pressure or if @q is dead. * * Must be called with @q->queue_lock held and, - * Returns %NULL on failure, with @q->queue_lock held. - * Returns !%NULL on success, with @q->queue_lock *not held*. + * Returns ERR_PTR on failure, with @q->queue_lock held. + * Returns request pointer on success, with @q->queue_lock *not held*. */ static struct request *__get_request(struct request_list *rl, int rw_flags, struct bio *bio, gfp_t gfp_mask) @@ -949,7 +944,7 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, int may_queue; if (unlikely(blk_queue_dying(q))) - return NULL; + return ERR_PTR(-ENODEV); may_queue = elv_may_queue(q, rw_flags); if (may_queue == ELV_MQUEUE_NO) @@ -974,7 +969,7 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, * process is not a "batcher", and not * exempted by the IO scheduler */ - return NULL; + return ERR_PTR(-ENOMEM); } } } @@ -992,7 +987,7 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, * allocated with any setting of ->nr_requests */ if (rl->count[is_sync] >= (3 * q->nr_requests / 2)) - return NULL; + return ERR_PTR(-ENOMEM); q->nr_rqs[is_sync]++; rl->count[is_sync]++; @@ -1065,8 +1060,8 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, * shouldn't stall IO. Treat this request as !elvpriv. This will * disturb iosched and blkcg but weird is bettern than dead. */ - printk_ratelimited(KERN_WARNING "%s: request aux data allocation failed, iosched may be disturbed\n", - dev_name(q->backing_dev_info.dev)); + printk_ratelimited(KERN_WARNING "%s: dev %s: request aux data allocation failed, iosched may be disturbed\n", + __func__, dev_name(q->backing_dev_info.dev)); rq->cmd_flags &= ~REQ_ELVPRIV; rq->elv.icq = NULL; @@ -1097,7 +1092,7 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, rq_starved: if (unlikely(rl->count[is_sync] == 0)) rl->starved[is_sync] = 1; - return NULL; + return ERR_PTR(-ENOMEM); } /** @@ -1111,8 +1106,8 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, * function keeps retrying under memory pressure and fails iff @q is dead. * * Must be called with @q->queue_lock held and, - * Returns %NULL on failure, with @q->queue_lock held. - * Returns !%NULL on success, with @q->queue_lock *not held*. + * Returns ERR_PTR on failure, with @q->queue_lock held. + * Returns request pointer on success, with @q->queue_lock *not held*. */ static struct request *get_request(struct request_queue *q, int rw_flags, struct bio *bio, gfp_t gfp_mask) @@ -1125,12 +1120,12 @@ static struct request *get_request(struct request_queue *q, int rw_flags, rl = blk_get_rl(q, bio); /* transferred to @rq on success */ retry: rq = __get_request(rl, rw_flags, bio, gfp_mask); - if (rq) + if (!IS_ERR(rq)) return rq; if (!(gfp_mask & __GFP_WAIT) || unlikely(blk_queue_dying(q))) { blk_put_rl(rl); - return NULL; + return rq; } /* wait on @rl and retry */ @@ -1167,7 +1162,7 @@ static struct request *blk_old_get_request(struct request_queue *q, int rw, spin_lock_irq(q->queue_lock); rq = get_request(q, rw, NULL, gfp_mask); - if (!rq) + if (IS_ERR(rq)) spin_unlock_irq(q->queue_lock); /* q->queue_lock is unlocked at this point */ @@ -1219,8 +1214,8 @@ struct request *blk_make_request(struct request_queue *q, struct bio *bio, { struct request *rq = blk_get_request(q, bio_data_dir(bio), gfp_mask); - if (unlikely(!rq)) - return ERR_PTR(-ENOMEM); + if (IS_ERR(rq)) + return rq; blk_rq_set_block_pc(rq); @@ -1614,8 +1609,8 @@ void blk_queue_bio(struct request_queue *q, struct bio *bio) * Returns with the queue unlocked. */ req = get_request(q, rw_flags, bio, GFP_NOIO); - if (unlikely(!req)) { - bio_endio(bio, -ENODEV); /* @q is dead */ + if (IS_ERR(req)) { + bio_endio(bio, PTR_ERR(req)); /* @q is dead */ goto out_unlock; } @@ -2405,11 +2400,11 @@ bool blk_update_request(struct request *req, int error, unsigned int nr_bytes) { int total_bytes; + trace_block_rq_complete(req->q, req, nr_bytes); + if (!req->bio) return false; - trace_block_rq_complete(req->q, req, nr_bytes); - /* * For fs requests, rq is just carrier of independent bio's * and each partial completion should be handled separately. @@ -2449,8 +2444,8 @@ bool blk_update_request(struct request *req, int error, unsigned int nr_bytes) error_type = "I/O"; break; } - printk_ratelimited(KERN_ERR "end_request: %s error, dev %s, sector %llu\n", - error_type, req->rq_disk ? + printk_ratelimited(KERN_ERR "%s: %s error, dev %s, sector %llu\n", + __func__, error_type, req->rq_disk ? req->rq_disk->disk_name : "?", (unsigned long long)blk_rq_pos(req)); @@ -2931,7 +2926,7 @@ int blk_rq_prep_clone(struct request *rq, struct request *rq_src, blk_rq_init(NULL, rq); __rq_for_each_bio(bio_src, rq_src) { - bio = bio_clone_bioset(bio_src, gfp_mask, bs); + bio = bio_clone_fast(bio_src, gfp_mask, bs); if (!bio) goto free_and_out; diff --git a/block/blk-flush.c b/block/blk-flush.c index 3cb5e9e7108adc8473296d8fe762bb16338d17f4..20badd7b9d1b664f534a791b83435534f94a70ec 100644 --- a/block/blk-flush.c +++ b/block/blk-flush.c @@ -28,7 +28,7 @@ * * The actual execution of flush is double buffered. Whenever a request * needs to execute PRE or POSTFLUSH, it queues at - * q->flush_queue[q->flush_pending_idx]. Once certain criteria are met, a + * fq->flush_queue[fq->flush_pending_idx]. Once certain criteria are met, a * flush is issued and the pending_idx is toggled. When the flush * completes, all the requests which were pending are proceeded to the next * step. This allows arbitrary merging of different types of FLUSH/FUA @@ -91,7 +91,8 @@ enum { FLUSH_PENDING_TIMEOUT = 5 * HZ, }; -static bool blk_kick_flush(struct request_queue *q); +static bool blk_kick_flush(struct request_queue *q, + struct blk_flush_queue *fq); static unsigned int blk_flush_policy(unsigned int fflags, struct request *rq) { @@ -126,8 +127,6 @@ static void blk_flush_restore_request(struct request *rq) /* make @rq a normal request */ rq->cmd_flags &= ~REQ_FLUSH_SEQ; rq->end_io = rq->flush.saved_end_io; - - blk_clear_rq_complete(rq); } static bool blk_flush_queue_rq(struct request *rq, bool add_front) @@ -150,6 +149,7 @@ static bool blk_flush_queue_rq(struct request *rq, bool add_front) /** * blk_flush_complete_seq - complete flush sequence * @rq: FLUSH/FUA request being sequenced + * @fq: flush queue * @seq: sequences to complete (mask of %REQ_FSEQ_*, can be zero) * @error: whether an error occurred * @@ -157,16 +157,17 @@ static bool blk_flush_queue_rq(struct request *rq, bool add_front) * completion and trigger the next step. * * CONTEXT: - * spin_lock_irq(q->queue_lock or q->mq_flush_lock) + * spin_lock_irq(q->queue_lock or fq->mq_flush_lock) * * RETURNS: * %true if requests were added to the dispatch queue, %false otherwise. */ -static bool blk_flush_complete_seq(struct request *rq, unsigned int seq, - int error) +static bool blk_flush_complete_seq(struct request *rq, + struct blk_flush_queue *fq, + unsigned int seq, int error) { struct request_queue *q = rq->q; - struct list_head *pending = &q->flush_queue[q->flush_pending_idx]; + struct list_head *pending = &fq->flush_queue[fq->flush_pending_idx]; bool queued = false, kicked; BUG_ON(rq->flush.seq & seq); @@ -182,12 +183,12 @@ static bool blk_flush_complete_seq(struct request *rq, unsigned int seq, case REQ_FSEQ_POSTFLUSH: /* queue for flush */ if (list_empty(pending)) - q->flush_pending_since = jiffies; + fq->flush_pending_since = jiffies; list_move_tail(&rq->flush.list, pending); break; case REQ_FSEQ_DATA: - list_move_tail(&rq->flush.list, &q->flush_data_in_flight); + list_move_tail(&rq->flush.list, &fq->flush_data_in_flight); queued = blk_flush_queue_rq(rq, true); break; @@ -202,7 +203,7 @@ static bool blk_flush_complete_seq(struct request *rq, unsigned int seq, list_del_init(&rq->flush.list); blk_flush_restore_request(rq); if (q->mq_ops) - blk_mq_end_io(rq, error); + blk_mq_end_request(rq, error); else __blk_end_request_all(rq, error); break; @@ -211,7 +212,7 @@ static bool blk_flush_complete_seq(struct request *rq, unsigned int seq, BUG(); } - kicked = blk_kick_flush(q); + kicked = blk_kick_flush(q, fq); return kicked | queued; } @@ -222,17 +223,18 @@ static void flush_end_io(struct request *flush_rq, int error) bool queued = false; struct request *rq, *n; unsigned long flags = 0; + struct blk_flush_queue *fq = blk_get_flush_queue(q, flush_rq->mq_ctx); if (q->mq_ops) { - spin_lock_irqsave(&q->mq_flush_lock, flags); - q->flush_rq->tag = -1; + spin_lock_irqsave(&fq->mq_flush_lock, flags); + flush_rq->tag = -1; } - running = &q->flush_queue[q->flush_running_idx]; - BUG_ON(q->flush_pending_idx == q->flush_running_idx); + running = &fq->flush_queue[fq->flush_running_idx]; + BUG_ON(fq->flush_pending_idx == fq->flush_running_idx); /* account completion of the flush request */ - q->flush_running_idx ^= 1; + fq->flush_running_idx ^= 1; if (!q->mq_ops) elv_completed_request(q, flush_rq); @@ -242,7 +244,7 @@ static void flush_end_io(struct request *flush_rq, int error) unsigned int seq = blk_flush_cur_seq(rq); BUG_ON(seq != REQ_FSEQ_PREFLUSH && seq != REQ_FSEQ_POSTFLUSH); - queued |= blk_flush_complete_seq(rq, seq, error); + queued |= blk_flush_complete_seq(rq, fq, seq, error); } /* @@ -256,71 +258,81 @@ static void flush_end_io(struct request *flush_rq, int error) * directly into request_fn may confuse the driver. Always use * kblockd. */ - if (queued || q->flush_queue_delayed) { + if (queued || fq->flush_queue_delayed) { WARN_ON(q->mq_ops); blk_run_queue_async(q); } - q->flush_queue_delayed = 0; + fq->flush_queue_delayed = 0; if (q->mq_ops) - spin_unlock_irqrestore(&q->mq_flush_lock, flags); + spin_unlock_irqrestore(&fq->mq_flush_lock, flags); } /** * blk_kick_flush - consider issuing flush request * @q: request_queue being kicked + * @fq: flush queue * * Flush related states of @q have changed, consider issuing flush request. * Please read the comment at the top of this file for more info. * * CONTEXT: - * spin_lock_irq(q->queue_lock or q->mq_flush_lock) + * spin_lock_irq(q->queue_lock or fq->mq_flush_lock) * * RETURNS: * %true if flush was issued, %false otherwise. */ -static bool blk_kick_flush(struct request_queue *q) +static bool blk_kick_flush(struct request_queue *q, struct blk_flush_queue *fq) { - struct list_head *pending = &q->flush_queue[q->flush_pending_idx]; + struct list_head *pending = &fq->flush_queue[fq->flush_pending_idx]; struct request *first_rq = list_first_entry(pending, struct request, flush.list); + struct request *flush_rq = fq->flush_rq; /* C1 described at the top of this file */ - if (q->flush_pending_idx != q->flush_running_idx || list_empty(pending)) + if (fq->flush_pending_idx != fq->flush_running_idx || list_empty(pending)) return false; /* C2 and C3 */ - if (!list_empty(&q->flush_data_in_flight) && + if (!list_empty(&fq->flush_data_in_flight) && time_before(jiffies, - q->flush_pending_since + FLUSH_PENDING_TIMEOUT)) + fq->flush_pending_since + FLUSH_PENDING_TIMEOUT)) return false; /* * Issue flush and toggle pending_idx. This makes pending_idx * different from running_idx, which means flush is in flight. */ - q->flush_pending_idx ^= 1; + fq->flush_pending_idx ^= 1; - blk_rq_init(q, q->flush_rq); - if (q->mq_ops) - blk_mq_clone_flush_request(q->flush_rq, first_rq); + blk_rq_init(q, flush_rq); + + /* + * Borrow tag from the first request since they can't + * be in flight at the same time. + */ + if (q->mq_ops) { + flush_rq->mq_ctx = first_rq->mq_ctx; + flush_rq->tag = first_rq->tag; + } - q->flush_rq->cmd_type = REQ_TYPE_FS; - q->flush_rq->cmd_flags = WRITE_FLUSH | REQ_FLUSH_SEQ; - q->flush_rq->rq_disk = first_rq->rq_disk; - q->flush_rq->end_io = flush_end_io; + flush_rq->cmd_type = REQ_TYPE_FS; + flush_rq->cmd_flags = WRITE_FLUSH | REQ_FLUSH_SEQ; + flush_rq->rq_disk = first_rq->rq_disk; + flush_rq->end_io = flush_end_io; - return blk_flush_queue_rq(q->flush_rq, false); + return blk_flush_queue_rq(flush_rq, false); } static void flush_data_end_io(struct request *rq, int error) { struct request_queue *q = rq->q; + struct blk_flush_queue *fq = blk_get_flush_queue(q, NULL); /* * After populating an empty queue, kick it to avoid stall. Read * the comment in flush_end_io(). */ - if (blk_flush_complete_seq(rq, REQ_FSEQ_DATA, error)) + if (blk_flush_complete_seq(rq, fq, REQ_FSEQ_DATA, error)) blk_run_queue_async(q); } @@ -328,20 +340,20 @@ static void mq_flush_data_end_io(struct request *rq, int error) { struct request_queue *q = rq->q; struct blk_mq_hw_ctx *hctx; - struct blk_mq_ctx *ctx; + struct blk_mq_ctx *ctx = rq->mq_ctx; unsigned long flags; + struct blk_flush_queue *fq = blk_get_flush_queue(q, ctx); - ctx = rq->mq_ctx; hctx = q->mq_ops->map_queue(q, ctx->cpu); /* * After populating an empty queue, kick it to avoid stall. Read * the comment in flush_end_io(). */ - spin_lock_irqsave(&q->mq_flush_lock, flags); - if (blk_flush_complete_seq(rq, REQ_FSEQ_DATA, error)) + spin_lock_irqsave(&fq->mq_flush_lock, flags); + if (blk_flush_complete_seq(rq, fq, REQ_FSEQ_DATA, error)) blk_mq_run_hw_queue(hctx, true); - spin_unlock_irqrestore(&q->mq_flush_lock, flags); + spin_unlock_irqrestore(&fq->mq_flush_lock, flags); } /** @@ -361,6 +373,7 @@ void blk_insert_flush(struct request *rq) struct request_queue *q = rq->q; unsigned int fflags = q->flush_flags; /* may change, cache */ unsigned int policy = blk_flush_policy(fflags, rq); + struct blk_flush_queue *fq = blk_get_flush_queue(q, rq->mq_ctx); /* * @policy now records what operations need to be done. Adjust @@ -378,7 +391,7 @@ void blk_insert_flush(struct request *rq) */ if (!policy) { if (q->mq_ops) - blk_mq_end_io(rq, 0); + blk_mq_end_request(rq, 0); else __blk_end_bidi_request(rq, 0, 0, 0); return; @@ -411,14 +424,14 @@ void blk_insert_flush(struct request *rq) if (q->mq_ops) { rq->end_io = mq_flush_data_end_io; - spin_lock_irq(&q->mq_flush_lock); - blk_flush_complete_seq(rq, REQ_FSEQ_ACTIONS & ~policy, 0); - spin_unlock_irq(&q->mq_flush_lock); + spin_lock_irq(&fq->mq_flush_lock); + blk_flush_complete_seq(rq, fq, REQ_FSEQ_ACTIONS & ~policy, 0); + spin_unlock_irq(&fq->mq_flush_lock); return; } rq->end_io = flush_data_end_io; - blk_flush_complete_seq(rq, REQ_FSEQ_ACTIONS & ~policy, 0); + blk_flush_complete_seq(rq, fq, REQ_FSEQ_ACTIONS & ~policy, 0); } /** @@ -474,7 +487,43 @@ int blkdev_issue_flush(struct block_device *bdev, gfp_t gfp_mask, } EXPORT_SYMBOL(blkdev_issue_flush); -void blk_mq_init_flush(struct request_queue *q) +struct blk_flush_queue *blk_alloc_flush_queue(struct request_queue *q, + int node, int cmd_size) +{ + struct blk_flush_queue *fq; + int rq_sz = sizeof(struct request); + + fq = kzalloc_node(sizeof(*fq), GFP_KERNEL, node); + if (!fq) + goto fail; + + if (q->mq_ops) { + spin_lock_init(&fq->mq_flush_lock); + rq_sz = round_up(rq_sz + cmd_size, cache_line_size()); + } + + fq->flush_rq = kzalloc_node(rq_sz, GFP_KERNEL, node); + if (!fq->flush_rq) + goto fail_rq; + + INIT_LIST_HEAD(&fq->flush_queue[0]); + INIT_LIST_HEAD(&fq->flush_queue[1]); + INIT_LIST_HEAD(&fq->flush_data_in_flight); + + return fq; + + fail_rq: + kfree(fq); + fail: + return NULL; +} + +void blk_free_flush_queue(struct blk_flush_queue *fq) { - spin_lock_init(&q->mq_flush_lock); + /* bio based request queue hasn't flush queue */ + if (!fq) + return; + + kfree(fq->flush_rq); + kfree(fq); } diff --git a/block/blk-integrity.c b/block/blk-integrity.c index 7fbab84399e6c9c602c52c8157596535a2ce2e05..79ffb4855af048462c8c44b3d0a7a493926989bc 100644 --- a/block/blk-integrity.c +++ b/block/blk-integrity.c @@ -154,10 +154,10 @@ int blk_integrity_compare(struct gendisk *gd1, struct gendisk *gd2) if (!b1 || !b2) return -1; - if (b1->sector_size != b2->sector_size) { - printk(KERN_ERR "%s: %s/%s sector sz %u != %u\n", __func__, - gd1->disk_name, gd2->disk_name, - b1->sector_size, b2->sector_size); + if (b1->interval != b2->interval) { + pr_err("%s: %s/%s protection interval %u != %u\n", + __func__, gd1->disk_name, gd2->disk_name, + b1->interval, b2->interval); return -1; } @@ -186,37 +186,53 @@ int blk_integrity_compare(struct gendisk *gd1, struct gendisk *gd2) } EXPORT_SYMBOL(blk_integrity_compare); -int blk_integrity_merge_rq(struct request_queue *q, struct request *req, - struct request *next) +bool blk_integrity_merge_rq(struct request_queue *q, struct request *req, + struct request *next) { - if (blk_integrity_rq(req) != blk_integrity_rq(next)) - return -1; + if (blk_integrity_rq(req) == 0 && blk_integrity_rq(next) == 0) + return true; + + if (blk_integrity_rq(req) == 0 || blk_integrity_rq(next) == 0) + return false; + + if (bio_integrity(req->bio)->bip_flags != + bio_integrity(next->bio)->bip_flags) + return false; if (req->nr_integrity_segments + next->nr_integrity_segments > q->limits.max_integrity_segments) - return -1; + return false; - return 0; + return true; } EXPORT_SYMBOL(blk_integrity_merge_rq); -int blk_integrity_merge_bio(struct request_queue *q, struct request *req, - struct bio *bio) +bool blk_integrity_merge_bio(struct request_queue *q, struct request *req, + struct bio *bio) { int nr_integrity_segs; struct bio *next = bio->bi_next; + if (blk_integrity_rq(req) == 0 && bio_integrity(bio) == NULL) + return true; + + if (blk_integrity_rq(req) == 0 || bio_integrity(bio) == NULL) + return false; + + if (bio_integrity(req->bio)->bip_flags != bio_integrity(bio)->bip_flags) + return false; + bio->bi_next = NULL; nr_integrity_segs = blk_rq_count_integrity_sg(q, bio); bio->bi_next = next; if (req->nr_integrity_segments + nr_integrity_segs > q->limits.max_integrity_segments) - return -1; + return false; req->nr_integrity_segments += nr_integrity_segs; - return 0; + return true; } EXPORT_SYMBOL(blk_integrity_merge_bio); @@ -269,42 +285,48 @@ static ssize_t integrity_tag_size_show(struct blk_integrity *bi, char *page) return sprintf(page, "0\n"); } -static ssize_t integrity_read_store(struct blk_integrity *bi, - const char *page, size_t count) +static ssize_t integrity_verify_store(struct blk_integrity *bi, + const char *page, size_t count) { char *p = (char *) page; unsigned long val = simple_strtoul(p, &p, 10); if (val) - bi->flags |= INTEGRITY_FLAG_READ; + bi->flags |= BLK_INTEGRITY_VERIFY; else - bi->flags &= ~INTEGRITY_FLAG_READ; + bi->flags &= ~BLK_INTEGRITY_VERIFY; return count; } -static ssize_t integrity_read_show(struct blk_integrity *bi, char *page) +static ssize_t integrity_verify_show(struct blk_integrity *bi, char *page) { - return sprintf(page, "%d\n", (bi->flags & INTEGRITY_FLAG_READ) != 0); + return sprintf(page, "%d\n", (bi->flags & BLK_INTEGRITY_VERIFY) != 0); } -static ssize_t integrity_write_store(struct blk_integrity *bi, - const char *page, size_t count) +static ssize_t integrity_generate_store(struct blk_integrity *bi, + const char *page, size_t count) { char *p = (char *) page; unsigned long val = simple_strtoul(p, &p, 10); if (val) - bi->flags |= INTEGRITY_FLAG_WRITE; + bi->flags |= BLK_INTEGRITY_GENERATE; else - bi->flags &= ~INTEGRITY_FLAG_WRITE; + bi->flags &= ~BLK_INTEGRITY_GENERATE; return count; } -static ssize_t integrity_write_show(struct blk_integrity *bi, char *page) +static ssize_t integrity_generate_show(struct blk_integrity *bi, char *page) +{ + return sprintf(page, "%d\n", (bi->flags & BLK_INTEGRITY_GENERATE) != 0); +} + +static ssize_t integrity_device_show(struct blk_integrity *bi, char *page) { - return sprintf(page, "%d\n", (bi->flags & INTEGRITY_FLAG_WRITE) != 0); + return sprintf(page, "%u\n", + (bi->flags & BLK_INTEGRITY_DEVICE_CAPABLE) != 0); } static struct integrity_sysfs_entry integrity_format_entry = { @@ -317,23 +339,29 @@ static struct integrity_sysfs_entry integrity_tag_size_entry = { .show = integrity_tag_size_show, }; -static struct integrity_sysfs_entry integrity_read_entry = { +static struct integrity_sysfs_entry integrity_verify_entry = { .attr = { .name = "read_verify", .mode = S_IRUGO | S_IWUSR }, - .show = integrity_read_show, - .store = integrity_read_store, + .show = integrity_verify_show, + .store = integrity_verify_store, }; -static struct integrity_sysfs_entry integrity_write_entry = { +static struct integrity_sysfs_entry integrity_generate_entry = { .attr = { .name = "write_generate", .mode = S_IRUGO | S_IWUSR }, - .show = integrity_write_show, - .store = integrity_write_store, + .show = integrity_generate_show, + .store = integrity_generate_store, +}; + +static struct integrity_sysfs_entry integrity_device_entry = { + .attr = { .name = "device_is_integrity_capable", .mode = S_IRUGO }, + .show = integrity_device_show, }; static struct attribute *integrity_attrs[] = { &integrity_format_entry.attr, &integrity_tag_size_entry.attr, - &integrity_read_entry.attr, - &integrity_write_entry.attr, + &integrity_verify_entry.attr, + &integrity_generate_entry.attr, + &integrity_device_entry.attr, NULL, }; @@ -406,8 +434,8 @@ int blk_integrity_register(struct gendisk *disk, struct blk_integrity *template) kobject_uevent(&bi->kobj, KOBJ_ADD); - bi->flags |= INTEGRITY_FLAG_READ | INTEGRITY_FLAG_WRITE; - bi->sector_size = queue_logical_block_size(disk->queue); + bi->flags |= BLK_INTEGRITY_VERIFY | BLK_INTEGRITY_GENERATE; + bi->interval = queue_logical_block_size(disk->queue); disk->integrity = bi; } else bi = disk->integrity; @@ -418,9 +446,8 @@ int blk_integrity_register(struct gendisk *disk, struct blk_integrity *template) bi->generate_fn = template->generate_fn; bi->verify_fn = template->verify_fn; bi->tuple_size = template->tuple_size; - bi->set_tag_fn = template->set_tag_fn; - bi->get_tag_fn = template->get_tag_fn; bi->tag_size = template->tag_size; + bi->flags |= template->flags; } else bi->name = bi_unsupported_name; diff --git a/block/blk-merge.c b/block/blk-merge.c index 77881798f7930bd4c1df4ea3cc4a0ab3f30ddc80..ba99351c0f58898cb83bd336299c773230e9e73f 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -97,14 +97,18 @@ void blk_recalc_rq_segments(struct request *rq) void blk_recount_segments(struct request_queue *q, struct bio *bio) { - if (test_bit(QUEUE_FLAG_NO_SG_MERGE, &q->queue_flags) && + bool no_sg_merge = !!test_bit(QUEUE_FLAG_NO_SG_MERGE, + &q->queue_flags); + + if (no_sg_merge && !bio_flagged(bio, BIO_CLONED) && bio->bi_vcnt < queue_max_segments(q)) bio->bi_phys_segments = bio->bi_vcnt; else { struct bio *nxt = bio->bi_next; bio->bi_next = NULL; - bio->bi_phys_segments = __blk_recalc_rq_segments(q, bio, false); + bio->bi_phys_segments = __blk_recalc_rq_segments(q, bio, + no_sg_merge); bio->bi_next = nxt; } @@ -313,7 +317,7 @@ static inline int ll_new_hw_segment(struct request_queue *q, if (req->nr_phys_segments + nr_phys_segs > queue_max_segments(q)) goto no_merge; - if (bio_integrity(bio) && blk_integrity_merge_bio(q, req, bio)) + if (blk_integrity_merge_bio(q, req, bio) == false) goto no_merge; /* @@ -410,7 +414,7 @@ static int ll_merge_requests_fn(struct request_queue *q, struct request *req, if (total_phys_segments > queue_max_segments(q)) return 0; - if (blk_integrity_rq(req) && blk_integrity_merge_rq(q, req, next)) + if (blk_integrity_merge_rq(q, req, next) == false) return 0; /* Merge is OK... */ @@ -590,7 +594,7 @@ bool blk_rq_merge_ok(struct request *rq, struct bio *bio) return false; /* only merge integrity protected bio into ditto rq */ - if (bio_integrity(bio) != blk_integrity_rq(rq)) + if (blk_integrity_merge_bio(rq->q, rq, bio) == false) return false; /* must be using the same buffer */ diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c index c1b92426c95e28139134e51a68de2f724f8979f5..8317175a3009cb994d66f1f6128de6d9591ee249 100644 --- a/block/blk-mq-tag.c +++ b/block/blk-mq-tag.c @@ -351,15 +351,12 @@ static void bt_clear_tag(struct blk_mq_bitmap_tags *bt, unsigned int tag) return; wait_cnt = atomic_dec_return(&bs->wait_cnt); + if (unlikely(wait_cnt < 0)) + wait_cnt = atomic_inc_return(&bs->wait_cnt); if (wait_cnt == 0) { -wake: atomic_add(bt->wake_cnt, &bs->wait_cnt); bt_index_atomic_inc(&bt->wake_index); wake_up(&bs->wait); - } else if (wait_cnt < 0) { - wait_cnt = atomic_inc_return(&bs->wait_cnt); - if (!wait_cnt) - goto wake; } } @@ -392,45 +389,37 @@ void blk_mq_put_tag(struct blk_mq_hw_ctx *hctx, unsigned int tag, __blk_mq_put_reserved_tag(tags, tag); } -static void bt_for_each_free(struct blk_mq_bitmap_tags *bt, - unsigned long *free_map, unsigned int off) +static void bt_for_each(struct blk_mq_hw_ctx *hctx, + struct blk_mq_bitmap_tags *bt, unsigned int off, + busy_iter_fn *fn, void *data, bool reserved) { - int i; + struct request *rq; + int bit, i; for (i = 0; i < bt->map_nr; i++) { struct blk_align_bitmap *bm = &bt->map[i]; - int bit = 0; - do { - bit = find_next_zero_bit(&bm->word, bm->depth, bit); - if (bit >= bm->depth) - break; - - __set_bit(bit + off, free_map); - bit++; - } while (1); + for (bit = find_first_bit(&bm->word, bm->depth); + bit < bm->depth; + bit = find_next_bit(&bm->word, bm->depth, bit + 1)) { + rq = blk_mq_tag_to_rq(hctx->tags, off + bit); + if (rq->q == hctx->queue) + fn(hctx, rq, data, reserved); + } off += (1 << bt->bits_per_word); } } -void blk_mq_tag_busy_iter(struct blk_mq_tags *tags, - void (*fn)(void *, unsigned long *), void *data) +void blk_mq_tag_busy_iter(struct blk_mq_hw_ctx *hctx, busy_iter_fn *fn, + void *priv) { - unsigned long *tag_map; - size_t map_size; - - map_size = ALIGN(tags->nr_tags, BITS_PER_LONG) / BITS_PER_LONG; - tag_map = kzalloc(map_size * sizeof(unsigned long), GFP_ATOMIC); - if (!tag_map) - return; + struct blk_mq_tags *tags = hctx->tags; - bt_for_each_free(&tags->bitmap_tags, tag_map, tags->nr_reserved_tags); if (tags->nr_reserved_tags) - bt_for_each_free(&tags->breserved_tags, tag_map, 0); - - fn(data, tag_map); - kfree(tag_map); + bt_for_each(hctx, &tags->breserved_tags, 0, fn, priv, true); + bt_for_each(hctx, &tags->bitmap_tags, tags->nr_reserved_tags, fn, priv, + false); } EXPORT_SYMBOL(blk_mq_tag_busy_iter); @@ -463,8 +452,8 @@ static void bt_update_count(struct blk_mq_bitmap_tags *bt, } bt->wake_cnt = BT_WAIT_BATCH; - if (bt->wake_cnt > depth / 4) - bt->wake_cnt = max(1U, depth / 4); + if (bt->wake_cnt > depth / BT_WAIT_QUEUES) + bt->wake_cnt = max(1U, depth / BT_WAIT_QUEUES); bt->depth = depth; } diff --git a/block/blk-mq.c b/block/blk-mq.c index 38f4a165640dd0aba321bb3196d8b1d1996635c1..68929bad9a6a4048151f485cc1e44db2d655fc0f 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -223,9 +224,11 @@ struct request *blk_mq_alloc_request(struct request_queue *q, int rw, gfp_t gfp, struct blk_mq_hw_ctx *hctx; struct request *rq; struct blk_mq_alloc_data alloc_data; + int ret; - if (blk_mq_queue_enter(q)) - return NULL; + ret = blk_mq_queue_enter(q); + if (ret) + return ERR_PTR(ret); ctx = blk_mq_get_ctx(q); hctx = q->mq_ops->map_queue(q, ctx->cpu); @@ -245,6 +248,8 @@ struct request *blk_mq_alloc_request(struct request_queue *q, int rw, gfp_t gfp, ctx = alloc_data.ctx; } blk_mq_put_ctx(ctx); + if (!rq) + return ERR_PTR(-EWOULDBLOCK); return rq; } EXPORT_SYMBOL(blk_mq_alloc_request); @@ -276,27 +281,7 @@ void blk_mq_free_request(struct request *rq) __blk_mq_free_request(hctx, ctx, rq); } -/* - * Clone all relevant state from a request that has been put on hold in - * the flush state machine into the preallocated flush request that hangs - * off the request queue. - * - * For a driver the flush request should be invisible, that's why we are - * impersonating the original request here. - */ -void blk_mq_clone_flush_request(struct request *flush_rq, - struct request *orig_rq) -{ - struct blk_mq_hw_ctx *hctx = - orig_rq->q->mq_ops->map_queue(orig_rq->q, orig_rq->mq_ctx->cpu); - - flush_rq->mq_ctx = orig_rq->mq_ctx; - flush_rq->tag = orig_rq->tag; - memcpy(blk_mq_rq_to_pdu(flush_rq), blk_mq_rq_to_pdu(orig_rq), - hctx->cmd_size); -} - -inline void __blk_mq_end_io(struct request *rq, int error) +inline void __blk_mq_end_request(struct request *rq, int error) { blk_account_io_done(rq); @@ -308,15 +293,15 @@ inline void __blk_mq_end_io(struct request *rq, int error) blk_mq_free_request(rq); } } -EXPORT_SYMBOL(__blk_mq_end_io); +EXPORT_SYMBOL(__blk_mq_end_request); -void blk_mq_end_io(struct request *rq, int error) +void blk_mq_end_request(struct request *rq, int error) { if (blk_update_request(rq, error, blk_rq_bytes(rq))) BUG(); - __blk_mq_end_io(rq, error); + __blk_mq_end_request(rq, error); } -EXPORT_SYMBOL(blk_mq_end_io); +EXPORT_SYMBOL(blk_mq_end_request); static void __blk_mq_complete_request_remote(void *data) { @@ -356,7 +341,7 @@ void __blk_mq_complete_request(struct request *rq) struct request_queue *q = rq->q; if (!q->softirq_done_fn) - blk_mq_end_io(rq, rq->errors); + blk_mq_end_request(rq, rq->errors); else blk_mq_ipi_complete_request(rq); } @@ -380,7 +365,7 @@ void blk_mq_complete_request(struct request *rq) } EXPORT_SYMBOL(blk_mq_complete_request); -static void blk_mq_start_request(struct request *rq, bool last) +void blk_mq_start_request(struct request *rq) { struct request_queue *q = rq->q; @@ -417,35 +402,24 @@ static void blk_mq_start_request(struct request *rq, bool last) */ rq->nr_phys_segments++; } - - /* - * Flag the last request in the series so that drivers know when IO - * should be kicked off, if they don't do it on a per-request basis. - * - * Note: the flag isn't the only condition drivers should do kick off. - * If drive is busy, the last request might not have the bit set. - */ - if (last) - rq->cmd_flags |= REQ_END; } +EXPORT_SYMBOL(blk_mq_start_request); static void __blk_mq_requeue_request(struct request *rq) { struct request_queue *q = rq->q; trace_block_rq_requeue(q, rq); - clear_bit(REQ_ATOM_STARTED, &rq->atomic_flags); - - rq->cmd_flags &= ~REQ_END; - if (q->dma_drain_size && blk_rq_bytes(rq)) - rq->nr_phys_segments--; + if (test_and_clear_bit(REQ_ATOM_STARTED, &rq->atomic_flags)) { + if (q->dma_drain_size && blk_rq_bytes(rq)) + rq->nr_phys_segments--; + } } void blk_mq_requeue_request(struct request *rq) { __blk_mq_requeue_request(rq); - blk_clear_rq_complete(rq); BUG_ON(blk_queued_rq(rq)); blk_mq_add_to_requeue_list(rq, true); @@ -514,78 +488,35 @@ void blk_mq_kick_requeue_list(struct request_queue *q) } EXPORT_SYMBOL(blk_mq_kick_requeue_list); -static inline bool is_flush_request(struct request *rq, unsigned int tag) +static inline bool is_flush_request(struct request *rq, + struct blk_flush_queue *fq, unsigned int tag) { return ((rq->cmd_flags & REQ_FLUSH_SEQ) && - rq->q->flush_rq->tag == tag); + fq->flush_rq->tag == tag); } struct request *blk_mq_tag_to_rq(struct blk_mq_tags *tags, unsigned int tag) { struct request *rq = tags->rqs[tag]; + /* mq_ctx of flush rq is always cloned from the corresponding req */ + struct blk_flush_queue *fq = blk_get_flush_queue(rq->q, rq->mq_ctx); - if (!is_flush_request(rq, tag)) + if (!is_flush_request(rq, fq, tag)) return rq; - return rq->q->flush_rq; + return fq->flush_rq; } EXPORT_SYMBOL(blk_mq_tag_to_rq); struct blk_mq_timeout_data { - struct blk_mq_hw_ctx *hctx; - unsigned long *next; - unsigned int *next_set; + unsigned long next; + unsigned int next_set; }; -static void blk_mq_timeout_check(void *__data, unsigned long *free_tags) +void blk_mq_rq_timed_out(struct request *req, bool reserved) { - struct blk_mq_timeout_data *data = __data; - struct blk_mq_hw_ctx *hctx = data->hctx; - unsigned int tag; - - /* It may not be in flight yet (this is where - * the REQ_ATOMIC_STARTED flag comes in). The requests are - * statically allocated, so we know it's always safe to access the - * memory associated with a bit offset into ->rqs[]. - */ - tag = 0; - do { - struct request *rq; - - tag = find_next_zero_bit(free_tags, hctx->tags->nr_tags, tag); - if (tag >= hctx->tags->nr_tags) - break; - - rq = blk_mq_tag_to_rq(hctx->tags, tag++); - if (rq->q != hctx->queue) - continue; - if (!test_bit(REQ_ATOM_STARTED, &rq->atomic_flags)) - continue; - - blk_rq_check_expired(rq, data->next, data->next_set); - } while (1); -} - -static void blk_mq_hw_ctx_check_timeout(struct blk_mq_hw_ctx *hctx, - unsigned long *next, - unsigned int *next_set) -{ - struct blk_mq_timeout_data data = { - .hctx = hctx, - .next = next, - .next_set = next_set, - }; - - /* - * Ask the tagging code to iterate busy requests, so we can - * check them for timeout. - */ - blk_mq_tag_busy_iter(hctx->tags, blk_mq_timeout_check, &data); -} - -static enum blk_eh_timer_return blk_mq_rq_timed_out(struct request *rq) -{ - struct request_queue *q = rq->q; + struct blk_mq_ops *ops = req->q->mq_ops; + enum blk_eh_timer_return ret = BLK_EH_RESET_TIMER; /* * We know that complete is set at this point. If STARTED isn't set @@ -596,21 +527,54 @@ static enum blk_eh_timer_return blk_mq_rq_timed_out(struct request *rq) * we both flags will get cleared. So check here again, and ignore * a timeout event with a request that isn't active. */ - if (!test_bit(REQ_ATOM_STARTED, &rq->atomic_flags)) - return BLK_EH_NOT_HANDLED; + if (!test_bit(REQ_ATOM_STARTED, &req->atomic_flags)) + return; + + if (ops->timeout) + ret = ops->timeout(req, reserved); + + switch (ret) { + case BLK_EH_HANDLED: + __blk_mq_complete_request(req); + break; + case BLK_EH_RESET_TIMER: + blk_add_timer(req); + blk_clear_rq_complete(req); + break; + case BLK_EH_NOT_HANDLED: + break; + default: + printk(KERN_ERR "block: bad eh return: %d\n", ret); + break; + } +} + +static void blk_mq_check_expired(struct blk_mq_hw_ctx *hctx, + struct request *rq, void *priv, bool reserved) +{ + struct blk_mq_timeout_data *data = priv; - if (!q->mq_ops->timeout) - return BLK_EH_RESET_TIMER; + if (!test_bit(REQ_ATOM_STARTED, &rq->atomic_flags)) + return; - return q->mq_ops->timeout(rq); + if (time_after_eq(jiffies, rq->deadline)) { + if (!blk_mark_rq_complete(rq)) + blk_mq_rq_timed_out(rq, reserved); + } else if (!data->next_set || time_after(data->next, rq->deadline)) { + data->next = rq->deadline; + data->next_set = 1; + } } -static void blk_mq_rq_timer(unsigned long data) +static void blk_mq_rq_timer(unsigned long priv) { - struct request_queue *q = (struct request_queue *) data; + struct request_queue *q = (struct request_queue *)priv; + struct blk_mq_timeout_data data = { + .next = 0, + .next_set = 0, + }; struct blk_mq_hw_ctx *hctx; - unsigned long next = 0; - int i, next_set = 0; + int i; queue_for_each_hw_ctx(q, hctx, i) { /* @@ -620,12 +584,12 @@ static void blk_mq_rq_timer(unsigned long data) if (!hctx->nr_ctx || !hctx->tags) continue; - blk_mq_hw_ctx_check_timeout(hctx, &next, &next_set); + blk_mq_tag_busy_iter(hctx, blk_mq_check_expired, &data); } - if (next_set) { - next = blk_rq_timeout(round_jiffies_up(next)); - mod_timer(&q->timeout, next); + if (data.next_set) { + data.next = blk_rq_timeout(round_jiffies_up(data.next)); + mod_timer(&q->timeout, data.next); } else { queue_for_each_hw_ctx(q, hctx, i) blk_mq_tag_idle(hctx); @@ -751,9 +715,7 @@ static void __blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx) rq = list_first_entry(&rq_list, struct request, queuelist); list_del_init(&rq->queuelist); - blk_mq_start_request(rq, list_empty(&rq_list)); - - ret = q->mq_ops->queue_rq(hctx, rq); + ret = q->mq_ops->queue_rq(hctx, rq, list_empty(&rq_list)); switch (ret) { case BLK_MQ_RQ_QUEUE_OK: queued++; @@ -766,7 +728,7 @@ static void __blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx) pr_err("blk-mq: bad return on queue: %d\n", ret); case BLK_MQ_RQ_QUEUE_ERROR: rq->errors = -EIO; - blk_mq_end_io(rq, rq->errors); + blk_mq_end_request(rq, rq->errors); break; } @@ -1194,14 +1156,13 @@ static void blk_mq_make_request(struct request_queue *q, struct bio *bio) int ret; blk_mq_bio_to_request(rq, bio); - blk_mq_start_request(rq, true); /* * For OK queue, we are done. For error, kill it. Any other * error (busy), just add it to our list as we previously * would have done */ - ret = q->mq_ops->queue_rq(data.hctx, rq); + ret = q->mq_ops->queue_rq(data.hctx, rq, true); if (ret == BLK_MQ_RQ_QUEUE_OK) goto done; else { @@ -1209,7 +1170,7 @@ static void blk_mq_make_request(struct request_queue *q, struct bio *bio) if (ret == BLK_MQ_RQ_QUEUE_ERROR) { rq->errors = -EIO; - blk_mq_end_io(rq, rq->errors); + blk_mq_end_request(rq, rq->errors); goto done; } } @@ -1531,6 +1492,28 @@ static int blk_mq_hctx_notify(void *data, unsigned long action, return NOTIFY_OK; } +static void blk_mq_exit_hctx(struct request_queue *q, + struct blk_mq_tag_set *set, + struct blk_mq_hw_ctx *hctx, unsigned int hctx_idx) +{ + unsigned flush_start_tag = set->queue_depth; + + blk_mq_tag_idle(hctx); + + if (set->ops->exit_request) + set->ops->exit_request(set->driver_data, + hctx->fq->flush_rq, hctx_idx, + flush_start_tag + hctx_idx); + + if (set->ops->exit_hctx) + set->ops->exit_hctx(hctx, hctx_idx); + + blk_mq_unregister_cpu_notifier(&hctx->cpu_notifier); + blk_free_flush_queue(hctx->fq); + kfree(hctx->ctxs); + blk_mq_free_bitmap(&hctx->ctx_map); +} + static void blk_mq_exit_hw_queues(struct request_queue *q, struct blk_mq_tag_set *set, int nr_queue) { @@ -1540,17 +1523,8 @@ static void blk_mq_exit_hw_queues(struct request_queue *q, queue_for_each_hw_ctx(q, hctx, i) { if (i == nr_queue) break; - - blk_mq_tag_idle(hctx); - - if (set->ops->exit_hctx) - set->ops->exit_hctx(hctx, i); - - blk_mq_unregister_cpu_notifier(&hctx->cpu_notifier); - kfree(hctx->ctxs); - blk_mq_free_bitmap(&hctx->ctx_map); + blk_mq_exit_hctx(q, set, hctx, i); } - } static void blk_mq_free_hw_queues(struct request_queue *q, @@ -1565,53 +1539,88 @@ static void blk_mq_free_hw_queues(struct request_queue *q, } } -static int blk_mq_init_hw_queues(struct request_queue *q, - struct blk_mq_tag_set *set) +static int blk_mq_init_hctx(struct request_queue *q, + struct blk_mq_tag_set *set, + struct blk_mq_hw_ctx *hctx, unsigned hctx_idx) { - struct blk_mq_hw_ctx *hctx; - unsigned int i; + int node; + unsigned flush_start_tag = set->queue_depth; + + node = hctx->numa_node; + if (node == NUMA_NO_NODE) + node = hctx->numa_node = set->numa_node; + + INIT_DELAYED_WORK(&hctx->run_work, blk_mq_run_work_fn); + INIT_DELAYED_WORK(&hctx->delay_work, blk_mq_delay_work_fn); + spin_lock_init(&hctx->lock); + INIT_LIST_HEAD(&hctx->dispatch); + hctx->queue = q; + hctx->queue_num = hctx_idx; + hctx->flags = set->flags; + hctx->cmd_size = set->cmd_size; + + blk_mq_init_cpu_notifier(&hctx->cpu_notifier, + blk_mq_hctx_notify, hctx); + blk_mq_register_cpu_notifier(&hctx->cpu_notifier); + + hctx->tags = set->tags[hctx_idx]; /* - * Initialize hardware queues + * Allocate space for all possible cpus to avoid allocation at + * runtime */ - queue_for_each_hw_ctx(q, hctx, i) { - int node; + hctx->ctxs = kmalloc_node(nr_cpu_ids * sizeof(void *), + GFP_KERNEL, node); + if (!hctx->ctxs) + goto unregister_cpu_notifier; - node = hctx->numa_node; - if (node == NUMA_NO_NODE) - node = hctx->numa_node = set->numa_node; + if (blk_mq_alloc_bitmap(&hctx->ctx_map, node)) + goto free_ctxs; - INIT_DELAYED_WORK(&hctx->run_work, blk_mq_run_work_fn); - INIT_DELAYED_WORK(&hctx->delay_work, blk_mq_delay_work_fn); - spin_lock_init(&hctx->lock); - INIT_LIST_HEAD(&hctx->dispatch); - hctx->queue = q; - hctx->queue_num = i; - hctx->flags = set->flags; - hctx->cmd_size = set->cmd_size; + hctx->nr_ctx = 0; - blk_mq_init_cpu_notifier(&hctx->cpu_notifier, - blk_mq_hctx_notify, hctx); - blk_mq_register_cpu_notifier(&hctx->cpu_notifier); + if (set->ops->init_hctx && + set->ops->init_hctx(hctx, set->driver_data, hctx_idx)) + goto free_bitmap; - hctx->tags = set->tags[i]; + hctx->fq = blk_alloc_flush_queue(q, hctx->numa_node, set->cmd_size); + if (!hctx->fq) + goto exit_hctx; - /* - * Allocate space for all possible cpus to avoid allocation at - * runtime - */ - hctx->ctxs = kmalloc_node(nr_cpu_ids * sizeof(void *), - GFP_KERNEL, node); - if (!hctx->ctxs) - break; + if (set->ops->init_request && + set->ops->init_request(set->driver_data, + hctx->fq->flush_rq, hctx_idx, + flush_start_tag + hctx_idx, node)) + goto free_fq; - if (blk_mq_alloc_bitmap(&hctx->ctx_map, node)) - break; + return 0; - hctx->nr_ctx = 0; + free_fq: + kfree(hctx->fq); + exit_hctx: + if (set->ops->exit_hctx) + set->ops->exit_hctx(hctx, hctx_idx); + free_bitmap: + blk_mq_free_bitmap(&hctx->ctx_map); + free_ctxs: + kfree(hctx->ctxs); + unregister_cpu_notifier: + blk_mq_unregister_cpu_notifier(&hctx->cpu_notifier); + + return -1; +} + +static int blk_mq_init_hw_queues(struct request_queue *q, + struct blk_mq_tag_set *set) +{ + struct blk_mq_hw_ctx *hctx; + unsigned int i; - if (set->ops->init_hctx && - set->ops->init_hctx(hctx, set->driver_data, i)) + /* + * Initialize hardware queues + */ + queue_for_each_hw_ctx(q, hctx, i) { + if (blk_mq_init_hctx(q, set, hctx, i)) break; } @@ -1765,6 +1774,16 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set) if (!ctx) return ERR_PTR(-ENOMEM); + /* + * If a crashdump is active, then we are potentially in a very + * memory constrained environment. Limit us to 1 queue and + * 64 tags to prevent using too much memory. + */ + if (is_kdump_kernel()) { + set->nr_hw_queues = 1; + set->queue_depth = min(64U, set->queue_depth); + } + hctxs = kmalloc_node(set->nr_hw_queues * sizeof(*hctxs), GFP_KERNEL, set->numa_node); @@ -1783,7 +1802,8 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set) if (!hctxs[i]) goto err_hctxs; - if (!zalloc_cpumask_var(&hctxs[i]->cpumask, GFP_KERNEL)) + if (!zalloc_cpumask_var_node(&hctxs[i]->cpumask, GFP_KERNEL, + node)) goto err_hctxs; atomic_set(&hctxs[i]->nr_active, 0); @@ -1830,7 +1850,6 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set) else blk_queue_make_request(q, blk_sq_make_request); - blk_queue_rq_timed_out(q, blk_mq_rq_timed_out); if (set->timeout) blk_queue_rq_timeout(q, set->timeout); @@ -1842,17 +1861,10 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set) if (set->ops->complete) blk_queue_softirq_done(q, set->ops->complete); - blk_mq_init_flush(q); blk_mq_init_cpu_queues(q, set->nr_hw_queues); - q->flush_rq = kzalloc(round_up(sizeof(struct request) + - set->cmd_size, cache_line_size()), - GFP_KERNEL); - if (!q->flush_rq) - goto err_hw; - if (blk_mq_init_hw_queues(q, set)) - goto err_flush_rq; + goto err_hw; mutex_lock(&all_q_mutex); list_add_tail(&q->all_q_node, &all_q_list); @@ -1864,8 +1876,6 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set) return q; -err_flush_rq: - kfree(q->flush_rq); err_hw: blk_cleanup_queue(q); err_hctxs: diff --git a/block/blk-mq.h b/block/blk-mq.h index ca4964a6295d48490f22e25f513c39165af190a4..d567d5283ffa788640e15163167530f874fc1a80 100644 --- a/block/blk-mq.h +++ b/block/blk-mq.h @@ -27,7 +27,6 @@ struct blk_mq_ctx { void __blk_mq_complete_request(struct request *rq); void blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx, bool async); -void blk_mq_init_flush(struct request_queue *q); void blk_mq_freeze_queue(struct request_queue *q); void blk_mq_free_queue(struct request_queue *q); void blk_mq_clone_flush_request(struct request *flush_rq, @@ -60,6 +59,8 @@ extern int blk_mq_hw_queue_to_node(unsigned int *map, unsigned int); extern int blk_mq_sysfs_register(struct request_queue *q); extern void blk_mq_sysfs_unregister(struct request_queue *q); +extern void blk_mq_rq_timed_out(struct request *req, bool reserved); + /* * Basic implementation of sparser bitmap, allowing the user to spread * the bits over more cachelines. diff --git a/block/blk-settings.c b/block/blk-settings.c index f1a1795a56836be34f672817fe1007572b4d56af..aa02247d227e530223d15e35ecabdacc2b801ce1 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -574,7 +574,7 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b, bottom = max(b->physical_block_size, b->io_min) + alignment; /* Verify that top and bottom intervals line up */ - if (max(top, bottom) & (min(top, bottom) - 1)) { + if (max(top, bottom) % min(top, bottom)) { t->misaligned = 1; ret = -1; } @@ -619,7 +619,7 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b, /* Find lowest common alignment_offset */ t->alignment_offset = lcm(t->alignment_offset, alignment) - & (max(t->physical_block_size, t->io_min) - 1); + % max(t->physical_block_size, t->io_min); /* Verify that new alignment_offset is on a logical block boundary */ if (t->alignment_offset & (t->logical_block_size - 1)) { diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 521ae9089c50c704af07d643cd7cf3729e259327..1fac43408911acc505daae163820a5abbe1f4e45 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -519,8 +519,8 @@ static void blk_release_queue(struct kobject *kobj) if (q->mq_ops) blk_mq_free_queue(q); - - kfree(q->flush_rq); + else + blk_free_flush_queue(q->fq); blk_trace_shutdown(q); diff --git a/block/blk-timeout.c b/block/blk-timeout.c index 95a09590ccfda8366f92efbee7c31f1654c11637..56c025894cdf2d73f78c5346c9c5987d5deb0e37 100644 --- a/block/blk-timeout.c +++ b/block/blk-timeout.c @@ -90,10 +90,7 @@ static void blk_rq_timed_out(struct request *req) switch (ret) { case BLK_EH_HANDLED: /* Can we use req->errors here? */ - if (q->mq_ops) - __blk_mq_complete_request(req); - else - __blk_complete_request(req); + __blk_complete_request(req); break; case BLK_EH_RESET_TIMER: blk_add_timer(req); @@ -113,7 +110,7 @@ static void blk_rq_timed_out(struct request *req) } } -void blk_rq_check_expired(struct request *rq, unsigned long *next_timeout, +static void blk_rq_check_expired(struct request *rq, unsigned long *next_timeout, unsigned int *next_set) { if (time_after_eq(jiffies, rq->deadline)) { @@ -162,7 +159,10 @@ void blk_abort_request(struct request *req) if (blk_mark_rq_complete(req)) return; blk_delete_timer(req); - blk_rq_timed_out(req); + if (req->q->mq_ops) + blk_mq_rq_timed_out(req, false); + else + blk_rq_timed_out(req); } EXPORT_SYMBOL_GPL(blk_abort_request); @@ -190,7 +190,8 @@ void blk_add_timer(struct request *req) struct request_queue *q = req->q; unsigned long expiry; - if (!q->rq_timed_out_fn) + /* blk-mq has its own handler, so we don't need ->rq_timed_out_fn */ + if (!q->mq_ops && !q->rq_timed_out_fn) return; BUG_ON(!list_empty(&req->timeout_list)); diff --git a/block/blk.h b/block/blk.h index 6748c4f8d7a1a3db7b20a5f8bb1b6b651982c66e..43b036185712c25f66d04a3c9e49565297c94ccd 100644 --- a/block/blk.h +++ b/block/blk.h @@ -2,6 +2,8 @@ #define BLK_INTERNAL_H #include +#include +#include "blk-mq.h" /* Amount of time in which a process may batch requests */ #define BLK_BATCH_TIME (HZ/50UL) @@ -12,16 +14,44 @@ /* Max future timer expiry for timeouts */ #define BLK_MAX_TIMEOUT (5 * HZ) +struct blk_flush_queue { + unsigned int flush_queue_delayed:1; + unsigned int flush_pending_idx:1; + unsigned int flush_running_idx:1; + unsigned long flush_pending_since; + struct list_head flush_queue[2]; + struct list_head flush_data_in_flight; + struct request *flush_rq; + spinlock_t mq_flush_lock; +}; + extern struct kmem_cache *blk_requestq_cachep; extern struct kmem_cache *request_cachep; extern struct kobj_type blk_queue_ktype; extern struct ida blk_queue_ida; +static inline struct blk_flush_queue *blk_get_flush_queue( + struct request_queue *q, struct blk_mq_ctx *ctx) +{ + struct blk_mq_hw_ctx *hctx; + + if (!q->mq_ops) + return q->fq; + + hctx = q->mq_ops->map_queue(q, ctx->cpu); + + return hctx->fq; +} + static inline void __blk_get_queue(struct request_queue *q) { kobject_get(&q->kobj); } +struct blk_flush_queue *blk_alloc_flush_queue(struct request_queue *q, + int node, int cmd_size); +void blk_free_flush_queue(struct blk_flush_queue *q); + int blk_init_rl(struct request_list *rl, struct request_queue *q, gfp_t gfp_mask); void blk_exit_rl(struct request_list *rl); @@ -38,8 +68,6 @@ bool __blk_end_bidi_request(struct request *rq, int error, unsigned int nr_bytes, unsigned int bidi_bytes); void blk_rq_timed_out_timer(unsigned long data); -void blk_rq_check_expired(struct request *rq, unsigned long *next_timeout, - unsigned int *next_set); unsigned long blk_rq_timeout(unsigned long timeout); void blk_add_timer(struct request *req); void blk_delete_timer(struct request *); @@ -88,6 +116,7 @@ void blk_insert_flush(struct request *rq); static inline struct request *__elv_next_request(struct request_queue *q) { struct request *rq; + struct blk_flush_queue *fq = blk_get_flush_queue(q, NULL); while (1) { if (!list_empty(&q->queue_head)) { @@ -110,9 +139,9 @@ static inline struct request *__elv_next_request(struct request_queue *q) * should be restarted later. Please see flush_end_io() for * details. */ - if (q->flush_pending_idx != q->flush_running_idx && + if (fq->flush_pending_idx != fq->flush_running_idx && !queue_flush_queueable(q)) { - q->flush_queue_delayed = 1; + fq->flush_queue_delayed = 1; return NULL; } if (unlikely(blk_queue_bypass(q)) || diff --git a/block/bsg.c b/block/bsg.c index ff46addde5d8e0877d390901bb6a91bac591bb13..276e869e686cbbdb39112f19b163e353f0ddc3ef 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -270,8 +270,8 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr, fmode_t has_write_perm, * map scatter-gather elements separately and string them to request */ rq = blk_get_request(q, rw, GFP_KERNEL); - if (!rq) - return ERR_PTR(-ENOMEM); + if (IS_ERR(rq)) + return rq; blk_rq_set_block_pc(rq); ret = blk_fill_sgv4_hdr_rq(q, rq, hdr, bd, has_write_perm); @@ -285,8 +285,9 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr, fmode_t has_write_perm, } next_rq = blk_get_request(q, READ, GFP_KERNEL); - if (!next_rq) { - ret = -ENOMEM; + if (IS_ERR(next_rq)) { + ret = PTR_ERR(next_rq); + next_rq = NULL; goto out; } rq->next_rq = next_rq; diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 3f31cf9508e6204c144c2b665fb90aa67df3c9a2..6f2751d305dede39f138f77e8b110a42460b2182 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -299,7 +299,7 @@ struct cfq_io_cq { struct cfq_ttime ttime; int ioprio; /* the current ioprio */ #ifdef CONFIG_CFQ_GROUP_IOSCHED - uint64_t blkcg_id; /* the current blkcg ID */ + uint64_t blkcg_serial_nr; /* the current blkcg serial */ #endif }; @@ -3547,17 +3547,17 @@ static void check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio) { struct cfq_data *cfqd = cic_to_cfqd(cic); struct cfq_queue *sync_cfqq; - uint64_t id; + uint64_t serial_nr; rcu_read_lock(); - id = bio_blkcg(bio)->id; + serial_nr = bio_blkcg(bio)->css.serial_nr; rcu_read_unlock(); /* * Check whether blkcg has changed. The condition may trigger * spuriously on a newly created cic but there's no harm. */ - if (unlikely(!cfqd) || likely(cic->blkcg_id == id)) + if (unlikely(!cfqd) || likely(cic->blkcg_serial_nr == serial_nr)) return; sync_cfqq = cic_to_cfqq(cic, 1); @@ -3571,7 +3571,7 @@ static void check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio) cfq_put_queue(sync_cfqq); } - cic->blkcg_id = id; + cic->blkcg_serial_nr = serial_nr; } #else static inline void check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio) { } diff --git a/block/compat_ioctl.c b/block/compat_ioctl.c index 18b282ce361e12b20ac8952b1bfb1e7c178d1649..f678c733df404189bdff2a87a01225c6aa124816 100644 --- a/block/compat_ioctl.c +++ b/block/compat_ioctl.c @@ -709,8 +709,6 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) if (!arg) return -EINVAL; bdi = blk_get_backing_dev_info(bdev); - if (bdi == NULL) - return -ENOTTY; return compat_put_long(arg, (bdi->ra_pages * PAGE_CACHE_SIZE) / 512); case BLKROGET: /* compatible */ @@ -731,8 +729,6 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg) if (!capable(CAP_SYS_ADMIN)) return -EACCES; bdi = blk_get_backing_dev_info(bdev); - if (bdi == NULL) - return -ENOTTY; bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE; return 0; case BLKGETSIZE: diff --git a/block/ioctl.c b/block/ioctl.c index d6cda8147c91ea828ea0dcd45f21d4ec6773abd6..6c7bf903742f923c2f720869364da218ea1bbe95 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -356,8 +356,6 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, if (!arg) return -EINVAL; bdi = blk_get_backing_dev_info(bdev); - if (bdi == NULL) - return -ENOTTY; return put_long(arg, (bdi->ra_pages * PAGE_CACHE_SIZE) / 512); case BLKROGET: return put_int(arg, bdev_read_only(bdev) != 0); @@ -386,8 +384,6 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, if(!capable(CAP_SYS_ADMIN)) return -EACCES; bdi = blk_get_backing_dev_info(bdev); - if (bdi == NULL) - return -ENOTTY; bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE; return 0; case BLKBSZSET: diff --git a/block/partitions/mac.c b/block/partitions/mac.c index 76d8ba6379a9863673bcacd31946b6712d2c8d71..c2c48ec64b2709c5f252e164bc3397df0c439210 100644 --- a/block/partitions/mac.c +++ b/block/partitions/mac.c @@ -81,7 +81,7 @@ int mac_partition(struct parsed_partitions *state) be32_to_cpu(part->start_block) * (secsize/512), be32_to_cpu(part->block_count) * (secsize/512)); - if (!strnicmp(part->type, "Linux_RAID", 10)) + if (!strncasecmp(part->type, "Linux_RAID", 10)) state->parts[slot].flags = ADDPART_FLAG_RAID; #ifdef CONFIG_PPC_PMAC /* @@ -100,7 +100,7 @@ int mac_partition(struct parsed_partitions *state) goodness++; if (strcasecmp(part->type, "Apple_UNIX_SVR2") == 0 - || (strnicmp(part->type, "Linux", 5) == 0 + || (strncasecmp(part->type, "Linux", 5) == 0 && strcasecmp(part->type, "Linux_swap") != 0)) { int i, l; @@ -109,13 +109,13 @@ int mac_partition(struct parsed_partitions *state) if (strcmp(part->name, "/") == 0) goodness++; for (i = 0; i <= l - 4; ++i) { - if (strnicmp(part->name + i, "root", + if (strncasecmp(part->name + i, "root", 4) == 0) { goodness += 2; break; } } - if (strnicmp(part->name, "swap", 4) == 0) + if (strncasecmp(part->name, "swap", 4) == 0) goodness--; } diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index 9b8eaeca6a794b5be8e732567aceffbe6ab6ebdb..abb2e65b24ccb4cbd64e0194d5bdaf9751e2b145 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -316,8 +316,8 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk, ret = -ENOMEM; rq = blk_get_request(q, writing ? WRITE : READ, GFP_KERNEL); - if (!rq) - goto out; + if (IS_ERR(rq)) + return PTR_ERR(rq); blk_rq_set_block_pc(rq); if (hdr->cmd_len > BLK_MAX_CDB) { @@ -387,7 +387,6 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk, kfree(rq->cmd); out_put_request: blk_put_request(rq); -out: return ret; } @@ -457,8 +456,8 @@ int sg_scsi_ioctl(struct request_queue *q, struct gendisk *disk, fmode_t mode, } rq = blk_get_request(q, in_len ? WRITE : READ, __GFP_WAIT); - if (!rq) { - err = -ENOMEM; + if (IS_ERR(rq)) { + err = PTR_ERR(rq); goto error; } blk_rq_set_block_pc(rq); @@ -548,6 +547,8 @@ static int __blk_send_generic(struct request_queue *q, struct gendisk *bd_disk, int err; rq = blk_get_request(q, WRITE, __GFP_WAIT); + if (IS_ERR(rq)) + return PTR_ERR(rq); blk_rq_set_block_pc(rq); rq->timeout = BLK_DEFAULT_SG_TIMEOUT; rq->cmd[0] = cmd; diff --git a/block/t10-pi.c b/block/t10-pi.c new file mode 100644 index 0000000000000000000000000000000000000000..24d6e9715318e682e21607119358f9582038ceff --- /dev/null +++ b/block/t10-pi.c @@ -0,0 +1,197 @@ +/* + * t10_pi.c - Functions for generating and verifying T10 Protection + * Information. + * + * Copyright (C) 2007, 2008, 2014 Oracle Corporation + * Written by: Martin K. Petersen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + */ + +#include +#include +#include +#include + +typedef __be16 (csum_fn) (void *, unsigned int); + +static const __be16 APP_ESCAPE = (__force __be16) 0xffff; +static const __be32 REF_ESCAPE = (__force __be32) 0xffffffff; + +static __be16 t10_pi_crc_fn(void *data, unsigned int len) +{ + return cpu_to_be16(crc_t10dif(data, len)); +} + +static __be16 t10_pi_ip_fn(void *data, unsigned int len) +{ + return (__force __be16)ip_compute_csum(data, len); +} + +/* + * Type 1 and Type 2 protection use the same format: 16 bit guard tag, + * 16 bit app tag, 32 bit reference tag. Type 3 does not define the ref + * tag. + */ +static int t10_pi_generate(struct blk_integrity_iter *iter, csum_fn *fn, + unsigned int type) +{ + unsigned int i; + + for (i = 0 ; i < iter->data_size ; i += iter->interval) { + struct t10_pi_tuple *pi = iter->prot_buf; + + pi->guard_tag = fn(iter->data_buf, iter->interval); + pi->app_tag = 0; + + if (type == 1) + pi->ref_tag = cpu_to_be32(lower_32_bits(iter->seed)); + else + pi->ref_tag = 0; + + iter->data_buf += iter->interval; + iter->prot_buf += sizeof(struct t10_pi_tuple); + iter->seed++; + } + + return 0; +} + +static int t10_pi_verify(struct blk_integrity_iter *iter, csum_fn *fn, + unsigned int type) +{ + unsigned int i; + + for (i = 0 ; i < iter->data_size ; i += iter->interval) { + struct t10_pi_tuple *pi = iter->prot_buf; + __be16 csum; + + switch (type) { + case 1: + case 2: + if (pi->app_tag == APP_ESCAPE) + goto next; + + if (be32_to_cpu(pi->ref_tag) != + lower_32_bits(iter->seed)) { + pr_err("%s: ref tag error at location %llu " \ + "(rcvd %u)\n", iter->disk_name, + (unsigned long long) + iter->seed, be32_to_cpu(pi->ref_tag)); + return -EILSEQ; + } + break; + case 3: + if (pi->app_tag == APP_ESCAPE && + pi->ref_tag == REF_ESCAPE) + goto next; + break; + } + + csum = fn(iter->data_buf, iter->interval); + + if (pi->guard_tag != csum) { + pr_err("%s: guard tag error at sector %llu " \ + "(rcvd %04x, want %04x)\n", iter->disk_name, + (unsigned long long)iter->seed, + be16_to_cpu(pi->guard_tag), be16_to_cpu(csum)); + return -EILSEQ; + } + +next: + iter->data_buf += iter->interval; + iter->prot_buf += sizeof(struct t10_pi_tuple); + iter->seed++; + } + + return 0; +} + +static int t10_pi_type1_generate_crc(struct blk_integrity_iter *iter) +{ + return t10_pi_generate(iter, t10_pi_crc_fn, 1); +} + +static int t10_pi_type1_generate_ip(struct blk_integrity_iter *iter) +{ + return t10_pi_generate(iter, t10_pi_ip_fn, 1); +} + +static int t10_pi_type1_verify_crc(struct blk_integrity_iter *iter) +{ + return t10_pi_verify(iter, t10_pi_crc_fn, 1); +} + +static int t10_pi_type1_verify_ip(struct blk_integrity_iter *iter) +{ + return t10_pi_verify(iter, t10_pi_ip_fn, 1); +} + +static int t10_pi_type3_generate_crc(struct blk_integrity_iter *iter) +{ + return t10_pi_generate(iter, t10_pi_crc_fn, 3); +} + +static int t10_pi_type3_generate_ip(struct blk_integrity_iter *iter) +{ + return t10_pi_generate(iter, t10_pi_ip_fn, 3); +} + +static int t10_pi_type3_verify_crc(struct blk_integrity_iter *iter) +{ + return t10_pi_verify(iter, t10_pi_crc_fn, 3); +} + +static int t10_pi_type3_verify_ip(struct blk_integrity_iter *iter) +{ + return t10_pi_verify(iter, t10_pi_ip_fn, 3); +} + +struct blk_integrity t10_pi_type1_crc = { + .name = "T10-DIF-TYPE1-CRC", + .generate_fn = t10_pi_type1_generate_crc, + .verify_fn = t10_pi_type1_verify_crc, + .tuple_size = sizeof(struct t10_pi_tuple), + .tag_size = 0, +}; +EXPORT_SYMBOL(t10_pi_type1_crc); + +struct blk_integrity t10_pi_type1_ip = { + .name = "T10-DIF-TYPE1-IP", + .generate_fn = t10_pi_type1_generate_ip, + .verify_fn = t10_pi_type1_verify_ip, + .tuple_size = sizeof(struct t10_pi_tuple), + .tag_size = 0, +}; +EXPORT_SYMBOL(t10_pi_type1_ip); + +struct blk_integrity t10_pi_type3_crc = { + .name = "T10-DIF-TYPE3-CRC", + .generate_fn = t10_pi_type3_generate_crc, + .verify_fn = t10_pi_type3_verify_crc, + .tuple_size = sizeof(struct t10_pi_tuple), + .tag_size = 0, +}; +EXPORT_SYMBOL(t10_pi_type3_crc); + +struct blk_integrity t10_pi_type3_ip = { + .name = "T10-DIF-TYPE3-IP", + .generate_fn = t10_pi_type3_generate_ip, + .verify_fn = t10_pi_type3_verify_ip, + .tuple_size = sizeof(struct t10_pi_tuple), + .tag_size = 0, +}; +EXPORT_SYMBOL(t10_pi_type3_ip); diff --git a/crypto/hmac.c b/crypto/hmac.c index 8d9544cf8169fd30d12fdea1d6303cfd3f4e7158..e392219ddc61953054cd834b53a35aa3fc8eb9cd 100644 --- a/crypto/hmac.c +++ b/crypto/hmac.c @@ -52,20 +52,17 @@ static int hmac_setkey(struct crypto_shash *parent, struct hmac_ctx *ctx = align_ptr(opad + ss, crypto_tfm_ctx_alignment()); struct crypto_shash *hash = ctx->hash; - struct { - struct shash_desc shash; - char ctx[crypto_shash_descsize(hash)]; - } desc; + SHASH_DESC_ON_STACK(shash, hash); unsigned int i; - desc.shash.tfm = hash; - desc.shash.flags = crypto_shash_get_flags(parent) & - CRYPTO_TFM_REQ_MAY_SLEEP; + shash->tfm = hash; + shash->flags = crypto_shash_get_flags(parent) + & CRYPTO_TFM_REQ_MAY_SLEEP; if (keylen > bs) { int err; - err = crypto_shash_digest(&desc.shash, inkey, keylen, ipad); + err = crypto_shash_digest(shash, inkey, keylen, ipad); if (err) return err; @@ -81,12 +78,12 @@ static int hmac_setkey(struct crypto_shash *parent, opad[i] ^= 0x5c; } - return crypto_shash_init(&desc.shash) ?: - crypto_shash_update(&desc.shash, ipad, bs) ?: - crypto_shash_export(&desc.shash, ipad) ?: - crypto_shash_init(&desc.shash) ?: - crypto_shash_update(&desc.shash, opad, bs) ?: - crypto_shash_export(&desc.shash, opad); + return crypto_shash_init(shash) ?: + crypto_shash_update(shash, ipad, bs) ?: + crypto_shash_export(shash, ipad) ?: + crypto_shash_init(shash) ?: + crypto_shash_update(shash, opad, bs) ?: + crypto_shash_export(shash, opad); } static int hmac_export(struct shash_desc *pdesc, void *out) diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 9459dfd7357f563de32d6abe487793cbc8ac6570..29a0cbdd0d19b1ddca6ee34c402ac7263d1647d4 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -1678,16 +1678,14 @@ static int alg_test_crc32c(const struct alg_test_desc *desc, } do { - struct { - struct shash_desc shash; - char ctx[crypto_shash_descsize(tfm)]; - } sdesc; + SHASH_DESC_ON_STACK(shash, tfm); + u32 *ctx = (u32 *)shash_desc_ctx(shash); - sdesc.shash.tfm = tfm; - sdesc.shash.flags = 0; + shash->tfm = tfm; + shash->flags = 0; - *(u32 *)sdesc.ctx = le32_to_cpu(420553207); - err = crypto_shash_final(&sdesc.shash, (u8 *)&val); + *ctx = le32_to_cpu(420553207); + err = crypto_shash_final(shash, (u8 *)&val); if (err) { printk(KERN_ERR "alg: crc32c: Operation failed for " "%s: %d\n", driver, err); diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c index 4edb1a81f63f68e3f37680b7334a3d7b81788f96..38216b991474d41192ab0c41d9b2265f5fa98ae3 100644 --- a/drivers/ata/pata_arasan_cf.c +++ b/drivers/ata/pata_arasan_cf.c @@ -420,7 +420,7 @@ dma_xfer(struct arasan_cf_dev *acdev, dma_addr_t src, dma_addr_t dest, u32 len) /* Wait for DMA to complete */ if (!wait_for_completion_timeout(&acdev->dma_completion, TIMEOUT)) { - chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(chan); dev_err(acdev->host->dev, "wait_for_completion_timeout\n"); return -ETIMEDOUT; } @@ -928,8 +928,7 @@ static int arasan_cf_suspend(struct device *dev) struct arasan_cf_dev *acdev = host->ports[0]->private_data; if (acdev->dma_chan) - acdev->dma_chan->device->device_control(acdev->dma_chan, - DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(acdev->dma_chan); cf_exit(acdev); return ata_host_suspend(host, PMSG_SUSPEND); diff --git a/drivers/atm/lanai.c b/drivers/atm/lanai.c index fa7d701933bab7cbfd0fa60c2e113de8c255a597..93eaf8d944926a706c3fe5b5bf1baac6a49c5c7c 100644 --- a/drivers/atm/lanai.c +++ b/drivers/atm/lanai.c @@ -2614,27 +2614,7 @@ static struct pci_driver lanai_driver = { .probe = lanai_init_one, }; -static int __init lanai_module_init(void) -{ - int x; - - x = pci_register_driver(&lanai_driver); - if (x != 0) - printk(KERN_ERR DEV_LABEL ": no adapter found\n"); - return x; -} - -static void __exit lanai_module_exit(void) -{ - /* We'll only get called when all the interfaces are already - * gone, so there isn't much to do - */ - DPRINTK("cleanup_module()\n"); - pci_unregister_driver(&lanai_driver); -} - -module_init(lanai_module_init); -module_exit(lanai_module_exit); +module_pci_driver(lanai_driver); MODULE_AUTHOR("Mitchell Blank Jr "); MODULE_DESCRIPTION("Efficient Networks Speedstream 3010 driver"); diff --git a/drivers/base/core.c b/drivers/base/core.c index 28b808c73e8ed14344472188794b94a14efea72f..14d162952c3bc21bdadf3966ccef0c7b2c43df91 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1211,6 +1211,9 @@ void device_del(struct device *dev) */ if (platform_notify_remove) platform_notify_remove(dev); + if (dev->bus) + blocking_notifier_call_chain(&dev->bus->p->bus_notifier, + BUS_NOTIFY_REMOVED_DEVICE, dev); kobject_uevent(&dev->kobj, KOBJ_REMOVE); cleanup_device_parent(dev); kobject_del(&dev->kobj); diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c index 7d6e84a51424b269d6bfffa401f6788ac44997ed..55b83983a9c00c8b5984c887a0435ffb6e03fe1d 100644 --- a/drivers/base/dma-coherent.c +++ b/drivers/base/dma-coherent.c @@ -14,11 +14,14 @@ struct dma_coherent_mem { int size; int flags; unsigned long *bitmap; + spinlock_t spinlock; }; -int dma_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, - dma_addr_t device_addr, size_t size, int flags) +static int dma_init_coherent_memory(phys_addr_t phys_addr, dma_addr_t device_addr, + size_t size, int flags, + struct dma_coherent_mem **mem) { + struct dma_coherent_mem *dma_mem = NULL; void __iomem *mem_base = NULL; int pages = size >> PAGE_SHIFT; int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long); @@ -27,40 +30,77 @@ int dma_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, goto out; if (!size) goto out; - if (dev->dma_mem) - goto out; - - /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */ mem_base = ioremap(phys_addr, size); if (!mem_base) goto out; - dev->dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL); - if (!dev->dma_mem) + dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL); + if (!dma_mem) goto out; - dev->dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL); - if (!dev->dma_mem->bitmap) - goto free1_out; + dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL); + if (!dma_mem->bitmap) + goto out; + + dma_mem->virt_base = mem_base; + dma_mem->device_base = device_addr; + dma_mem->pfn_base = PFN_DOWN(phys_addr); + dma_mem->size = pages; + dma_mem->flags = flags; + spin_lock_init(&dma_mem->spinlock); - dev->dma_mem->virt_base = mem_base; - dev->dma_mem->device_base = device_addr; - dev->dma_mem->pfn_base = PFN_DOWN(phys_addr); - dev->dma_mem->size = pages; - dev->dma_mem->flags = flags; + *mem = dma_mem; if (flags & DMA_MEMORY_MAP) return DMA_MEMORY_MAP; return DMA_MEMORY_IO; - free1_out: - kfree(dev->dma_mem); - out: +out: + kfree(dma_mem); if (mem_base) iounmap(mem_base); return 0; } + +static void dma_release_coherent_memory(struct dma_coherent_mem *mem) +{ + if (!mem) + return; + iounmap(mem->virt_base); + kfree(mem->bitmap); + kfree(mem); +} + +static int dma_assign_coherent_memory(struct device *dev, + struct dma_coherent_mem *mem) +{ + if (dev->dma_mem) + return -EBUSY; + + dev->dma_mem = mem; + /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */ + + return 0; +} + +int dma_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, + dma_addr_t device_addr, size_t size, int flags) +{ + struct dma_coherent_mem *mem; + int ret; + + ret = dma_init_coherent_memory(phys_addr, device_addr, size, flags, + &mem); + if (ret == 0) + return 0; + + if (dma_assign_coherent_memory(dev, mem) == 0) + return ret; + + dma_release_coherent_memory(mem); + return 0; +} EXPORT_SYMBOL(dma_declare_coherent_memory); void dma_release_declared_memory(struct device *dev) @@ -69,10 +109,8 @@ void dma_release_declared_memory(struct device *dev) if (!mem) return; + dma_release_coherent_memory(mem); dev->dma_mem = NULL; - iounmap(mem->virt_base); - kfree(mem->bitmap); - kfree(mem); } EXPORT_SYMBOL(dma_release_declared_memory); @@ -80,6 +118,7 @@ void *dma_mark_declared_memory_occupied(struct device *dev, dma_addr_t device_addr, size_t size) { struct dma_coherent_mem *mem = dev->dma_mem; + unsigned long flags; int pos, err; size += device_addr & ~PAGE_MASK; @@ -87,8 +126,11 @@ void *dma_mark_declared_memory_occupied(struct device *dev, if (!mem) return ERR_PTR(-EINVAL); + spin_lock_irqsave(&mem->spinlock, flags); pos = (device_addr - mem->device_base) >> PAGE_SHIFT; err = bitmap_allocate_region(mem->bitmap, pos, get_order(size)); + spin_unlock_irqrestore(&mem->spinlock, flags); + if (err != 0) return ERR_PTR(err); return mem->virt_base + (pos << PAGE_SHIFT); @@ -115,6 +157,7 @@ int dma_alloc_from_coherent(struct device *dev, ssize_t size, { struct dma_coherent_mem *mem; int order = get_order(size); + unsigned long flags; int pageno; if (!dev) @@ -124,6 +167,7 @@ int dma_alloc_from_coherent(struct device *dev, ssize_t size, return 0; *ret = NULL; + spin_lock_irqsave(&mem->spinlock, flags); if (unlikely(size > (mem->size << PAGE_SHIFT))) goto err; @@ -138,10 +182,12 @@ int dma_alloc_from_coherent(struct device *dev, ssize_t size, *dma_handle = mem->device_base + (pageno << PAGE_SHIFT); *ret = mem->virt_base + (pageno << PAGE_SHIFT); memset(*ret, 0, size); + spin_unlock_irqrestore(&mem->spinlock, flags); return 1; err: + spin_unlock_irqrestore(&mem->spinlock, flags); /* * In the case where the allocation can not be satisfied from the * per-device area, try to fall back to generic memory if the @@ -171,8 +217,11 @@ int dma_release_from_coherent(struct device *dev, int order, void *vaddr) if (mem && vaddr >= mem->virt_base && vaddr < (mem->virt_base + (mem->size << PAGE_SHIFT))) { int page = (vaddr - mem->virt_base) >> PAGE_SHIFT; + unsigned long flags; + spin_lock_irqsave(&mem->spinlock, flags); bitmap_release_region(mem->bitmap, page, order); + spin_unlock_irqrestore(&mem->spinlock, flags); return 1; } return 0; @@ -218,3 +267,61 @@ int dma_mmap_from_coherent(struct device *dev, struct vm_area_struct *vma, return 0; } EXPORT_SYMBOL(dma_mmap_from_coherent); + +/* + * Support for reserved memory regions defined in device tree + */ +#ifdef CONFIG_OF_RESERVED_MEM +#include +#include +#include + +static int rmem_dma_device_init(struct reserved_mem *rmem, struct device *dev) +{ + struct dma_coherent_mem *mem = rmem->priv; + + if (!mem && + dma_init_coherent_memory(rmem->base, rmem->base, rmem->size, + DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE, + &mem) != DMA_MEMORY_MAP) { + pr_err("Reserved memory: failed to init DMA memory pool at %pa, size %ld MiB\n", + &rmem->base, (unsigned long)rmem->size / SZ_1M); + return -ENODEV; + } + rmem->priv = mem; + dma_assign_coherent_memory(dev, mem); + return 0; +} + +static void rmem_dma_device_release(struct reserved_mem *rmem, + struct device *dev) +{ + dev->dma_mem = NULL; +} + +static const struct reserved_mem_ops rmem_dma_ops = { + .device_init = rmem_dma_device_init, + .device_release = rmem_dma_device_release, +}; + +static int __init rmem_dma_setup(struct reserved_mem *rmem) +{ + unsigned long node = rmem->fdt_node; + + if (of_get_flat_dt_prop(node, "reusable", NULL)) + return -EINVAL; + +#ifdef CONFIG_ARM + if (!of_get_flat_dt_prop(node, "no-map", NULL)) { + pr_err("Reserved memory: regions without no-map are not yet supported\n"); + return -EINVAL; + } +#endif + + rmem->ops = &rmem_dma_ops; + pr_info("Reserved memory: created DMA memory pool at %pa, size %ld MiB\n", + &rmem->base, (unsigned long)rmem->size / SZ_1M); + return 0; +} +RESERVEDMEM_OF_DECLARE(dma, "shared-dma-pool", rmem_dma_setup); +#endif diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 6606abdf880c816ce407c486fbabcc27d602cef9..473ff48924015f07daee1c81ebe3d6436727493e 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -211,3 +211,69 @@ bool dma_release_from_contiguous(struct device *dev, struct page *pages, { return cma_release(dev_get_cma_area(dev), pages, count); } + +/* + * Support for reserved memory regions defined in device tree + */ +#ifdef CONFIG_OF_RESERVED_MEM +#include +#include +#include + +#undef pr_fmt +#define pr_fmt(fmt) fmt + +static void rmem_cma_device_init(struct reserved_mem *rmem, struct device *dev) +{ + dev_set_cma_area(dev, rmem->priv); +} + +static void rmem_cma_device_release(struct reserved_mem *rmem, + struct device *dev) +{ + dev_set_cma_area(dev, NULL); +} + +static const struct reserved_mem_ops rmem_cma_ops = { + .device_init = rmem_cma_device_init, + .device_release = rmem_cma_device_release, +}; + +static int __init rmem_cma_setup(struct reserved_mem *rmem) +{ + phys_addr_t align = PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order); + phys_addr_t mask = align - 1; + unsigned long node = rmem->fdt_node; + struct cma *cma; + int err; + + if (!of_get_flat_dt_prop(node, "reusable", NULL) || + of_get_flat_dt_prop(node, "no-map", NULL)) + return -EINVAL; + + if ((rmem->base & mask) || (rmem->size & mask)) { + pr_err("Reserved memory: incorrect alignment of CMA region\n"); + return -EINVAL; + } + + err = cma_init_reserved_mem(rmem->base, rmem->size, 0, &cma); + if (err) { + pr_err("Reserved memory: unable to setup CMA region\n"); + return err; + } + /* Architecture specific contiguous memory fixup. */ + dma_contiguous_early_fixup(rmem->base, rmem->size); + + if (of_get_flat_dt_prop(node, "linux,cma-default", NULL)) + dma_contiguous_set_default(cma); + + rmem->ops = &rmem_cma_ops; + rmem->priv = cma; + + pr_info("Reserved memory: created CMA memory pool at %pa, size %ld MiB\n", + &rmem->base, (unsigned long)rmem->size / SZ_1M); + + return 0; +} +RESERVEDMEM_OF_DECLARE(cma, "shared-dma-pool", rmem_cma_setup); +#endif diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c index d26a3fa6368849ce95d59a7847c8853c11861026..a2dfa169237d4b69423037feb8f3b3c2d9824a7e 100644 --- a/drivers/block/drbd/drbd_actlog.c +++ b/drivers/block/drbd/drbd_actlog.c @@ -158,14 +158,14 @@ static int _drbd_md_sync_page_io(struct drbd_device *device, if (bio_add_page(bio, device->md_io.page, size, 0) != size) goto out; bio->bi_private = device; - bio->bi_end_io = drbd_md_io_complete; + bio->bi_end_io = drbd_md_endio; bio->bi_rw = rw; if (!(rw & WRITE) && device->state.disk == D_DISKLESS && device->ldev == NULL) /* special case, drbd_md_read() during drbd_adm_attach(): no get_ldev */ ; else if (!get_ldev_if_state(device, D_ATTACHING)) { - /* Corresponding put_ldev in drbd_md_io_complete() */ + /* Corresponding put_ldev in drbd_md_endio() */ drbd_err(device, "ASSERT FAILED: get_ldev_if_state() == 1 in _drbd_md_sync_page_io()\n"); err = -ENODEV; goto out; diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c index 426c97aef9002193c6c7790040c95bdb2ec71eac..434c77dcc99e00c9da129278db2043117f7040d6 100644 --- a/drivers/block/drbd/drbd_bitmap.c +++ b/drivers/block/drbd/drbd_bitmap.c @@ -941,7 +941,7 @@ static void drbd_bm_aio_ctx_destroy(struct kref *kref) } /* bv_page may be a copy, or may be the original */ -static void bm_async_io_complete(struct bio *bio, int error) +static void drbd_bm_endio(struct bio *bio, int error) { struct drbd_bm_aio_ctx *ctx = bio->bi_private; struct drbd_device *device = ctx->device; @@ -1027,7 +1027,7 @@ static void bm_page_io_async(struct drbd_bm_aio_ctx *ctx, int page_nr) __must_ho * according to api. Do we want to assert that? */ bio_add_page(bio, page, len, 0); bio->bi_private = ctx; - bio->bi_end_io = bm_async_io_complete; + bio->bi_end_io = drbd_bm_endio; if (drbd_insert_fault(device, (rw & WRITE) ? DRBD_FAULT_MD_WR : DRBD_FAULT_MD_RD)) { bio->bi_rw |= rw; @@ -1125,7 +1125,7 @@ static int bm_rw(struct drbd_device *device, const unsigned int flags, unsigned } /* - * We initialize ctx->in_flight to one to make sure bm_async_io_complete + * We initialize ctx->in_flight to one to make sure drbd_bm_endio * will not set ctx->done early, and decrement / test it here. If there * are still some bios in flight, we need to wait for them here. * If all IO is done already (or nothing had been submitted), there is diff --git a/drivers/block/drbd/drbd_debugfs.c b/drivers/block/drbd/drbd_debugfs.c index 5c20b18540b866efb929074f7a94a039c72b09ea..900d4d3272d1af3d2caee19093b196f6b8971eb1 100644 --- a/drivers/block/drbd/drbd_debugfs.c +++ b/drivers/block/drbd/drbd_debugfs.c @@ -695,7 +695,7 @@ static void resync_dump_detail(struct seq_file *m, struct lc_element *e) { struct bm_extent *bme = lc_entry(e, struct bm_extent, lce); - seq_printf(m, "%5d %s %s %s\n", bme->rs_left, + seq_printf(m, "%5d %s %s %s", bme->rs_left, test_bit(BME_NO_WRITES, &bme->flags) ? "NO_WRITES" : "---------", test_bit(BME_LOCKED, &bme->flags) ? "LOCKED" : "------", test_bit(BME_PRIORITY, &bme->flags) ? "PRIORITY" : "--------" diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index 1a000016ccdfb8bfecf6769a09bb9c48a6aab215..9b22f8f01b5763646160af8c36013be2ccaba72f 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h @@ -61,8 +61,6 @@ # define __must_hold(x) #endif -#define __no_warn(lock, stmt) do { __acquire(lock); stmt; __release(lock); } while (0) - /* module parameter, defined in drbd_main.c */ extern unsigned int minor_count; extern bool disable_sendpage; @@ -1483,7 +1481,7 @@ extern int drbd_khelper(struct drbd_device *device, char *cmd); /* drbd_worker.c */ /* bi_end_io handlers */ -extern void drbd_md_io_complete(struct bio *bio, int error); +extern void drbd_md_endio(struct bio *bio, int error); extern void drbd_peer_request_endio(struct bio *bio, int error); extern void drbd_request_endio(struct bio *bio, int error); extern int drbd_worker(struct drbd_thread *thi); @@ -2100,16 +2098,19 @@ static inline bool is_sync_state(enum drbd_conns connection_state) /** * get_ldev() - Increase the ref count on device->ldev. Returns 0 if there is no ldev - * @M: DRBD device. + * @_device: DRBD device. + * @_min_state: Minimum device state required for success. * * You have to call put_ldev() when finished working with device->ldev. */ -#define get_ldev(M) __cond_lock(local, _get_ldev_if_state(M,D_INCONSISTENT)) -#define get_ldev_if_state(M,MINS) __cond_lock(local, _get_ldev_if_state(M,MINS)) +#define get_ldev_if_state(_device, _min_state) \ + (_get_ldev_if_state((_device), (_min_state)) ? \ + ({ __acquire(x); true; }) : false) +#define get_ldev(_device) get_ldev_if_state(_device, D_INCONSISTENT) static inline void put_ldev(struct drbd_device *device) { - enum drbd_disk_state ds = device->state.disk; + enum drbd_disk_state disk_state = device->state.disk; /* We must check the state *before* the atomic_dec becomes visible, * or we have a theoretical race where someone hitting zero, * while state still D_FAILED, will then see D_DISKLESS in the @@ -2122,10 +2123,10 @@ static inline void put_ldev(struct drbd_device *device) __release(local); D_ASSERT(device, i >= 0); if (i == 0) { - if (ds == D_DISKLESS) + if (disk_state == D_DISKLESS) /* even internal references gone, safe to destroy */ drbd_device_post_work(device, DESTROY_DISK); - if (ds == D_FAILED) + if (disk_state == D_FAILED) /* all application IO references gone. */ if (!test_and_set_bit(GOING_DISKLESS, &device->flags)) drbd_device_post_work(device, GO_DISKLESS); diff --git a/drivers/block/drbd/drbd_interval.c b/drivers/block/drbd/drbd_interval.c index 89c497c630b4ee60839571aabf59c9ad553fb229..51b25ad852515d2514e5215490e146d13371d6c4 100644 --- a/drivers/block/drbd/drbd_interval.c +++ b/drivers/block/drbd/drbd_interval.c @@ -37,40 +37,8 @@ compute_subtree_last(struct drbd_interval *node) return max; } -static void augment_propagate(struct rb_node *rb, struct rb_node *stop) -{ - while (rb != stop) { - struct drbd_interval *node = rb_entry(rb, struct drbd_interval, rb); - sector_t subtree_last = compute_subtree_last(node); - if (node->end == subtree_last) - break; - node->end = subtree_last; - rb = rb_parent(&node->rb); - } -} - -static void augment_copy(struct rb_node *rb_old, struct rb_node *rb_new) -{ - struct drbd_interval *old = rb_entry(rb_old, struct drbd_interval, rb); - struct drbd_interval *new = rb_entry(rb_new, struct drbd_interval, rb); - - new->end = old->end; -} - -static void augment_rotate(struct rb_node *rb_old, struct rb_node *rb_new) -{ - struct drbd_interval *old = rb_entry(rb_old, struct drbd_interval, rb); - struct drbd_interval *new = rb_entry(rb_new, struct drbd_interval, rb); - - new->end = old->end; - old->end = compute_subtree_last(old); -} - -static const struct rb_augment_callbacks augment_callbacks = { - augment_propagate, - augment_copy, - augment_rotate, -}; +RB_DECLARE_CALLBACKS(static, augment_callbacks, struct drbd_interval, rb, + sector_t, end, compute_subtree_last); /** * drbd_insert_interval - insert a new interval into a tree @@ -79,6 +47,7 @@ bool drbd_insert_interval(struct rb_root *root, struct drbd_interval *this) { struct rb_node **new = &root->rb_node, *parent = NULL; + sector_t this_end = this->sector + (this->size >> 9); BUG_ON(!IS_ALIGNED(this->size, 512)); @@ -87,6 +56,8 @@ drbd_insert_interval(struct rb_root *root, struct drbd_interval *this) rb_entry(*new, struct drbd_interval, rb); parent = *new; + if (here->end < this_end) + here->end = this_end; if (this->sector < here->sector) new = &(*new)->rb_left; else if (this->sector > here->sector) @@ -99,6 +70,7 @@ drbd_insert_interval(struct rb_root *root, struct drbd_interval *this) return false; } + this->end = this_end; rb_link_node(&this->rb, parent, new); rb_insert_augmented(&this->rb, root, &augment_callbacks); return true; diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 9b465bb68487b5c0e5a51f72d161ae7ade749453..973c185c9cfe60a30de818531a70eb76c2b03228 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -1622,13 +1622,13 @@ int drbd_send_dblock(struct drbd_peer_device *peer_device, struct drbd_request * struct drbd_socket *sock; struct p_data *p; unsigned int dp_flags = 0; - int dgs; + int digest_size; int err; sock = &peer_device->connection->data; p = drbd_prepare_command(peer_device, sock); - dgs = peer_device->connection->integrity_tfm ? - crypto_hash_digestsize(peer_device->connection->integrity_tfm) : 0; + digest_size = peer_device->connection->integrity_tfm ? + crypto_hash_digestsize(peer_device->connection->integrity_tfm) : 0; if (!p) return -EIO; @@ -1659,9 +1659,9 @@ int drbd_send_dblock(struct drbd_peer_device *peer_device, struct drbd_request * /* our digest is still only over the payload. * TRIM does not carry any payload. */ - if (dgs) + if (digest_size) drbd_csum_bio(peer_device->connection->integrity_tfm, req->master_bio, p + 1); - err = __send_command(peer_device->connection, device->vnr, sock, P_DATA, sizeof(*p) + dgs, NULL, req->i.size); + err = __send_command(peer_device->connection, device->vnr, sock, P_DATA, sizeof(*p) + digest_size, NULL, req->i.size); if (!err) { /* For protocol A, we have to memcpy the payload into * socket buffers, as we may complete right away @@ -1674,23 +1674,23 @@ int drbd_send_dblock(struct drbd_peer_device *peer_device, struct drbd_request * * out ok after sending on this side, but does not fit on the * receiving side, we sure have detected corruption elsewhere. */ - if (!(req->rq_state & (RQ_EXP_RECEIVE_ACK | RQ_EXP_WRITE_ACK)) || dgs) + if (!(req->rq_state & (RQ_EXP_RECEIVE_ACK | RQ_EXP_WRITE_ACK)) || digest_size) err = _drbd_send_bio(peer_device, req->master_bio); else err = _drbd_send_zc_bio(peer_device, req->master_bio); /* double check digest, sometimes buffers have been modified in flight. */ - if (dgs > 0 && dgs <= 64) { + if (digest_size > 0 && digest_size <= 64) { /* 64 byte, 512 bit, is the largest digest size * currently supported in kernel crypto. */ unsigned char digest[64]; drbd_csum_bio(peer_device->connection->integrity_tfm, req->master_bio, digest); - if (memcmp(p + 1, digest, dgs)) { + if (memcmp(p + 1, digest, digest_size)) { drbd_warn(device, "Digest mismatch, buffer modified by upper layers during write: %llus +%u\n", (unsigned long long)req->i.sector, req->i.size); } - } /* else if (dgs > 64) { + } /* else if (digest_size > 64) { ... Be noisy about digest too large ... } */ } @@ -1711,13 +1711,13 @@ int drbd_send_block(struct drbd_peer_device *peer_device, enum drbd_packet cmd, struct drbd_socket *sock; struct p_data *p; int err; - int dgs; + int digest_size; sock = &peer_device->connection->data; p = drbd_prepare_command(peer_device, sock); - dgs = peer_device->connection->integrity_tfm ? - crypto_hash_digestsize(peer_device->connection->integrity_tfm) : 0; + digest_size = peer_device->connection->integrity_tfm ? + crypto_hash_digestsize(peer_device->connection->integrity_tfm) : 0; if (!p) return -EIO; @@ -1725,9 +1725,9 @@ int drbd_send_block(struct drbd_peer_device *peer_device, enum drbd_packet cmd, p->block_id = peer_req->block_id; p->seq_num = 0; /* unused */ p->dp_flags = 0; - if (dgs) + if (digest_size) drbd_csum_ee(peer_device->connection->integrity_tfm, peer_req, p + 1); - err = __send_command(peer_device->connection, device->vnr, sock, cmd, sizeof(*p) + dgs, NULL, peer_req->i.size); + err = __send_command(peer_device->connection, device->vnr, sock, cmd, sizeof(*p) + digest_size, NULL, peer_req->i.size); if (!err) err = _drbd_send_zc_ee(peer_device, peer_req); mutex_unlock(&sock->mutex); /* locked by drbd_prepare_command() */ diff --git a/drivers/block/drbd/drbd_proc.c b/drivers/block/drbd/drbd_proc.c index 06e6147c76013602d2591cab96bb239afb073d17..3b10fa6cb039339a2301350862b7d87e95c6a0cb 100644 --- a/drivers/block/drbd/drbd_proc.c +++ b/drivers/block/drbd/drbd_proc.c @@ -142,10 +142,12 @@ static void drbd_syncer_progress(struct drbd_device *device, struct seq_file *se (unsigned long) Bit2KB(rs_left >> 10), (unsigned long) Bit2KB(rs_total >> 10)); else - seq_printf(seq, "(%lu/%lu)K\n\t", + seq_printf(seq, "(%lu/%lu)K", (unsigned long) Bit2KB(rs_left), (unsigned long) Bit2KB(rs_total)); + seq_printf(seq, "\n\t"); + /* see drivers/md/md.c * We do not want to overflow, so the order of operands and * the * 100 / 100 trick are important. We do a +1 to be diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index 9342b8da73ab517620dda3b38f9852e6c1219853..6960fb064731075ee86297082fc0a1cf4fcdcd2d 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -1371,9 +1371,9 @@ int drbd_submit_peer_request(struct drbd_device *device, struct bio *bio; struct page *page = peer_req->pages; sector_t sector = peer_req->i.sector; - unsigned ds = peer_req->i.size; + unsigned data_size = peer_req->i.size; unsigned n_bios = 0; - unsigned nr_pages = (ds + PAGE_SIZE -1) >> PAGE_SHIFT; + unsigned nr_pages = (data_size + PAGE_SIZE -1) >> PAGE_SHIFT; int err = -ENOMEM; if (peer_req->flags & EE_IS_TRIM_USE_ZEROOUT) { @@ -1388,7 +1388,7 @@ int drbd_submit_peer_request(struct drbd_device *device, list_add_tail(&peer_req->w.list, &device->active_ee); spin_unlock_irq(&device->resource->req_lock); if (blkdev_issue_zeroout(device->ldev->backing_bdev, - sector, ds >> 9, GFP_NOIO)) + sector, data_size >> 9, GFP_NOIO)) peer_req->flags |= EE_WAS_ERROR; drbd_endio_write_sec_final(peer_req); return 0; @@ -1426,12 +1426,12 @@ int drbd_submit_peer_request(struct drbd_device *device, ++n_bios; if (rw & REQ_DISCARD) { - bio->bi_iter.bi_size = ds; + bio->bi_iter.bi_size = data_size; goto submit; } page_chain_for_each(page) { - unsigned len = min_t(unsigned, ds, PAGE_SIZE); + unsigned len = min_t(unsigned, data_size, PAGE_SIZE); if (!bio_add_page(bio, page, len, 0)) { /* A single page must always be possible! * But in case it fails anyways, @@ -1446,11 +1446,11 @@ int drbd_submit_peer_request(struct drbd_device *device, } goto next_bio; } - ds -= len; + data_size -= len; sector += len >> 9; --nr_pages; } - D_ASSERT(device, ds == 0); + D_ASSERT(device, data_size == 0); submit: D_ASSERT(device, page == NULL); @@ -1591,24 +1591,24 @@ read_in_block(struct drbd_peer_device *peer_device, u64 id, sector_t sector, const sector_t capacity = drbd_get_capacity(device->this_bdev); struct drbd_peer_request *peer_req; struct page *page; - int dgs, ds, err; - unsigned int data_size = pi->size; + int digest_size, err; + unsigned int data_size = pi->size, ds; void *dig_in = peer_device->connection->int_dig_in; void *dig_vv = peer_device->connection->int_dig_vv; unsigned long *data; struct p_trim *trim = (pi->cmd == P_TRIM) ? pi->data : NULL; - dgs = 0; + digest_size = 0; if (!trim && peer_device->connection->peer_integrity_tfm) { - dgs = crypto_hash_digestsize(peer_device->connection->peer_integrity_tfm); + digest_size = crypto_hash_digestsize(peer_device->connection->peer_integrity_tfm); /* * FIXME: Receive the incoming digest into the receive buffer * here, together with its struct p_data? */ - err = drbd_recv_all_warn(peer_device->connection, dig_in, dgs); + err = drbd_recv_all_warn(peer_device->connection, dig_in, digest_size); if (err) return NULL; - data_size -= dgs; + data_size -= digest_size; } if (trim) { @@ -1661,16 +1661,16 @@ read_in_block(struct drbd_peer_device *peer_device, u64 id, sector_t sector, ds -= len; } - if (dgs) { + if (digest_size) { drbd_csum_ee(peer_device->connection->peer_integrity_tfm, peer_req, dig_vv); - if (memcmp(dig_in, dig_vv, dgs)) { + if (memcmp(dig_in, dig_vv, digest_size)) { drbd_err(device, "Digest integrity check FAILED: %llus +%u\n", (unsigned long long)sector, data_size); drbd_free_peer_req(device, peer_req); return NULL; } } - device->recv_cnt += data_size>>9; + device->recv_cnt += data_size >> 9; return peer_req; } @@ -1708,17 +1708,17 @@ static int recv_dless_read(struct drbd_peer_device *peer_device, struct drbd_req struct bio_vec bvec; struct bvec_iter iter; struct bio *bio; - int dgs, err, expect; + int digest_size, err, expect; void *dig_in = peer_device->connection->int_dig_in; void *dig_vv = peer_device->connection->int_dig_vv; - dgs = 0; + digest_size = 0; if (peer_device->connection->peer_integrity_tfm) { - dgs = crypto_hash_digestsize(peer_device->connection->peer_integrity_tfm); - err = drbd_recv_all_warn(peer_device->connection, dig_in, dgs); + digest_size = crypto_hash_digestsize(peer_device->connection->peer_integrity_tfm); + err = drbd_recv_all_warn(peer_device->connection, dig_in, digest_size); if (err) return err; - data_size -= dgs; + data_size -= digest_size; } /* optimistically update recv_cnt. if receiving fails below, @@ -1738,9 +1738,9 @@ static int recv_dless_read(struct drbd_peer_device *peer_device, struct drbd_req data_size -= expect; } - if (dgs) { + if (digest_size) { drbd_csum_bio(peer_device->connection->peer_integrity_tfm, bio, dig_vv); - if (memcmp(dig_in, dig_vv, dgs)) { + if (memcmp(dig_in, dig_vv, digest_size)) { drbd_err(peer_device, "Digest integrity check FAILED. Broken NICs?\n"); return -EINVAL; } @@ -5561,6 +5561,7 @@ int drbd_asender(struct drbd_thread *thi) * rv < expected: "woken" by signal during receive * rv == 0 : "connection shut down by peer" */ +received_more: if (likely(rv > 0)) { received += rv; buf += rv; @@ -5636,6 +5637,11 @@ int drbd_asender(struct drbd_thread *thi) expect = header_size; cmd = NULL; } + if (test_bit(SEND_PING, &connection->flags)) + continue; + rv = drbd_recv_short(connection->meta.socket, buf, expect-received, MSG_DONTWAIT); + if (rv > 0) + goto received_more; } if (0) { diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index c67717d572d16c89b1a4f74701b25c0a7ffeafbe..5a01c53dddeb873b5fdba6d4d06e3c9f18476a41 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c @@ -1645,6 +1645,6 @@ void request_timer_fn(unsigned long data) ? oldest_submit_jif + dt : now + et; nt = time_before(ent, dt) ? ent : dt; out: - spin_unlock_irq(&connection->resource->req_lock); + spin_unlock_irq(&device->resource->req_lock); mod_timer(&device->request_timer, nt); } diff --git a/drivers/block/drbd/drbd_state.c b/drivers/block/drbd/drbd_state.c index c35c0f001bb74333887d0e47c23a0598cd203bfe..84b11f887d730f3351f136f1bfffcfc873f83433 100644 --- a/drivers/block/drbd/drbd_state.c +++ b/drivers/block/drbd/drbd_state.c @@ -136,50 +136,50 @@ enum drbd_role conn_highest_peer(struct drbd_connection *connection) enum drbd_disk_state conn_highest_disk(struct drbd_connection *connection) { - enum drbd_disk_state ds = D_DISKLESS; + enum drbd_disk_state disk_state = D_DISKLESS; struct drbd_peer_device *peer_device; int vnr; rcu_read_lock(); idr_for_each_entry(&connection->peer_devices, peer_device, vnr) { struct drbd_device *device = peer_device->device; - ds = max_t(enum drbd_disk_state, ds, device->state.disk); + disk_state = max_t(enum drbd_disk_state, disk_state, device->state.disk); } rcu_read_unlock(); - return ds; + return disk_state; } enum drbd_disk_state conn_lowest_disk(struct drbd_connection *connection) { - enum drbd_disk_state ds = D_MASK; + enum drbd_disk_state disk_state = D_MASK; struct drbd_peer_device *peer_device; int vnr; rcu_read_lock(); idr_for_each_entry(&connection->peer_devices, peer_device, vnr) { struct drbd_device *device = peer_device->device; - ds = min_t(enum drbd_disk_state, ds, device->state.disk); + disk_state = min_t(enum drbd_disk_state, disk_state, device->state.disk); } rcu_read_unlock(); - return ds; + return disk_state; } enum drbd_disk_state conn_highest_pdsk(struct drbd_connection *connection) { - enum drbd_disk_state ds = D_DISKLESS; + enum drbd_disk_state disk_state = D_DISKLESS; struct drbd_peer_device *peer_device; int vnr; rcu_read_lock(); idr_for_each_entry(&connection->peer_devices, peer_device, vnr) { struct drbd_device *device = peer_device->device; - ds = max_t(enum drbd_disk_state, ds, device->state.pdsk); + disk_state = max_t(enum drbd_disk_state, disk_state, device->state.pdsk); } rcu_read_unlock(); - return ds; + return disk_state; } enum drbd_conns conn_lowest_conn(struct drbd_connection *connection) diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c index 50776b36282868415d7b48ebed66fbf1f5dec09f..d2d1f97511bd1fe03496834dea3503f2aacdb617 100644 --- a/drivers/block/drbd/drbd_worker.c +++ b/drivers/block/drbd/drbd_worker.c @@ -43,10 +43,10 @@ static int make_ov_request(struct drbd_device *, int); static int make_resync_request(struct drbd_device *, int); /* endio handlers: - * drbd_md_io_complete (defined here) + * drbd_md_endio (defined here) * drbd_request_endio (defined here) * drbd_peer_request_endio (defined here) - * bm_async_io_complete (defined in drbd_bitmap.c) + * drbd_bm_endio (defined in drbd_bitmap.c) * * For all these callbacks, note the following: * The callbacks will be called in irq context by the IDE drivers, @@ -65,7 +65,7 @@ rwlock_t global_state_lock; /* used for synchronous meta data and bitmap IO * submitted by drbd_md_sync_page_io() */ -void drbd_md_io_complete(struct bio *bio, int error) +void drbd_md_endio(struct bio *bio, int error) { struct drbd_device *device; @@ -1853,9 +1853,12 @@ static void drbd_ldev_destroy(struct drbd_device *device) device->resync = NULL; lc_destroy(device->act_log); device->act_log = NULL; - __no_warn(local, - drbd_free_ldev(device->ldev); - device->ldev = NULL;); + + __acquire(local); + drbd_free_ldev(device->ldev); + device->ldev = NULL; + __release(local); + clear_bit(GOING_DISKLESS, &device->flags); wake_up(&device->misc_wait); } @@ -1928,19 +1931,18 @@ void __update_timing_details( ++(*cb_nr); } -#define WORK_PENDING(work_bit, todo) (todo & (1UL << work_bit)) static void do_device_work(struct drbd_device *device, const unsigned long todo) { - if (WORK_PENDING(MD_SYNC, todo)) + if (test_bit(MD_SYNC, &todo)) do_md_sync(device); - if (WORK_PENDING(RS_DONE, todo) || - WORK_PENDING(RS_PROGRESS, todo)) - update_on_disk_bitmap(device, WORK_PENDING(RS_DONE, todo)); - if (WORK_PENDING(GO_DISKLESS, todo)) + if (test_bit(RS_DONE, &todo) || + test_bit(RS_PROGRESS, &todo)) + update_on_disk_bitmap(device, test_bit(RS_DONE, &todo)); + if (test_bit(GO_DISKLESS, &todo)) go_diskless(device); - if (WORK_PENDING(DESTROY_DISK, todo)) + if (test_bit(DESTROY_DISK, &todo)) drbd_ldev_destroy(device); - if (WORK_PENDING(RS_START, todo)) + if (test_bit(RS_START, &todo)) do_start_resync(device); } @@ -1992,22 +1994,13 @@ static bool dequeue_work_batch(struct drbd_work_queue *queue, struct list_head * return !list_empty(work_list); } -static bool dequeue_work_item(struct drbd_work_queue *queue, struct list_head *work_list) -{ - spin_lock_irq(&queue->q_lock); - if (!list_empty(&queue->q)) - list_move(queue->q.next, work_list); - spin_unlock_irq(&queue->q_lock); - return !list_empty(work_list); -} - static void wait_for_work(struct drbd_connection *connection, struct list_head *work_list) { DEFINE_WAIT(wait); struct net_conf *nc; int uncork, cork; - dequeue_work_item(&connection->sender_work, work_list); + dequeue_work_batch(&connection->sender_work, work_list); if (!list_empty(work_list)) return; @@ -2033,8 +2026,6 @@ static void wait_for_work(struct drbd_connection *connection, struct list_head * prepare_to_wait(&connection->sender_work.q_wait, &wait, TASK_INTERRUPTIBLE); spin_lock_irq(&connection->resource->req_lock); spin_lock(&connection->sender_work.q_lock); /* FIXME get rid of this one? */ - /* dequeue single item only, - * we still use drbd_queue_work_front() in some places */ if (!list_empty(&connection->sender_work.q)) list_splice_tail_init(&connection->sender_work.q, work_list); spin_unlock(&connection->sender_work.q_lock); /* FIXME get rid of this one? */ @@ -2121,7 +2112,7 @@ int drbd_worker(struct drbd_thread *thi) if (get_t_state(thi) != RUNNING) break; - while (!list_empty(&work_list)) { + if (!list_empty(&work_list)) { w = list_first_entry(&work_list, struct drbd_work, list); list_del_init(&w->list); update_worker_timing_details(connection, w->cb); @@ -2137,13 +2128,13 @@ int drbd_worker(struct drbd_thread *thi) update_worker_timing_details(connection, do_unqueued_work); do_unqueued_work(connection); } - while (!list_empty(&work_list)) { + if (!list_empty(&work_list)) { w = list_first_entry(&work_list, struct drbd_work, list); list_del_init(&w->list); update_worker_timing_details(connection, w->cb); w->cb(w, 1); - } - dequeue_work_batch(&connection->sender_work, &work_list); + } else + dequeue_work_batch(&connection->sender_work, &work_list); } while (!list_empty(&work_list) || test_bit(DEVICE_WORK_PENDING, &connection->flags)); rcu_read_lock(); diff --git a/drivers/block/hd.c b/drivers/block/hd.c index 8a290c08262f26079ecd53235b3ee8c5bc43f681..3abb121825bc579609b218c7288b0fcd39b6040d 100644 --- a/drivers/block/hd.c +++ b/drivers/block/hd.c @@ -694,16 +694,6 @@ static const struct block_device_operations hd_fops = { .getgeo = hd_getgeo, }; -/* - * This is the hard disk IRQ description. The IRQF_DISABLED in sa_flags - * means we run the IRQ-handler with interrupts disabled: this is bad for - * interrupt latency, but anything else has led to problems on some - * machines. - * - * We enable interrupts in some of the routines after making sure it's - * safe. - */ - static int __init hd_init(void) { int drive; @@ -761,7 +751,7 @@ static int __init hd_init(void) p->cyl, p->head, p->sect); } - if (request_irq(HD_IRQ, hd_interrupt, IRQF_DISABLED, "hd", NULL)) { + if (request_irq(HD_IRQ, hd_interrupt, 0, "hd", NULL)) { printk("hd: unable to get IRQ%d for the hard disk driver\n", HD_IRQ); goto out1; diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 5c8e7fe077458f31e83b0bcf65137b255e4a8336..1bd5f523f8fdac6bd452281ae4dc33f105d33055 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -247,7 +247,7 @@ static void mtip_async_complete(struct mtip_port *port, if (unlikely(cmd->unaligned)) up(&port->cmd_slot_unal); - blk_mq_end_io(rq, status ? -EIO : 0); + blk_mq_end_request(rq, status ? -EIO : 0); } /* @@ -3739,7 +3739,7 @@ static int mtip_submit_request(struct blk_mq_hw_ctx *hctx, struct request *rq) int err; err = mtip_send_trim(dd, blk_rq_pos(rq), blk_rq_sectors(rq)); - blk_mq_end_io(rq, err); + blk_mq_end_request(rq, err); return 0; } @@ -3775,13 +3775,16 @@ static bool mtip_check_unal_depth(struct blk_mq_hw_ctx *hctx, return false; } -static int mtip_queue_rq(struct blk_mq_hw_ctx *hctx, struct request *rq) +static int mtip_queue_rq(struct blk_mq_hw_ctx *hctx, struct request *rq, + bool last) { int ret; if (unlikely(mtip_check_unal_depth(hctx, rq))) return BLK_MQ_RQ_QUEUE_BUSY; + blk_mq_start_request(rq); + ret = mtip_submit_request(hctx, rq); if (likely(!ret)) return BLK_MQ_RQ_QUEUE_OK; @@ -3951,6 +3954,7 @@ static int mtip_block_initialize(struct driver_data *dd) /* Set device limits. */ set_bit(QUEUE_FLAG_NONROT, &dd->queue->queue_flags); + clear_bit(QUEUE_FLAG_ADD_RANDOM, &dd->queue->queue_flags); blk_queue_max_segments(dd->queue, MTIP_MAX_SG); blk_queue_physical_block_size(dd->queue, 4096); blk_queue_max_hw_sectors(dd->queue, 0xffff); diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index fb31b8ee4372323a4a93eddb0aa9141ea97d1021..4bc2a5cb9935fbc6f5256102bdf9c2c24b9e6d8c 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -847,6 +847,7 @@ static int __init nbd_init(void) * Tell the block layer that we are not a rotational device */ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, disk->queue); + queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, disk->queue); disk->queue->limits.discard_granularity = 512; disk->queue->limits.max_discard_sectors = UINT_MAX; disk->queue->limits.discard_zeroes_data = 0; diff --git a/drivers/block/null_blk.c b/drivers/block/null_blk.c index 00d469c7f9f79742c659011088b144d5bf4b6bef..2671a3f02f0cfb71831b049c52da9c0f46b20848 100644 --- a/drivers/block/null_blk.c +++ b/drivers/block/null_blk.c @@ -177,7 +177,7 @@ static void end_cmd(struct nullb_cmd *cmd) { switch (queue_mode) { case NULL_Q_MQ: - blk_mq_end_io(cmd->rq, 0); + blk_mq_end_request(cmd->rq, 0); return; case NULL_Q_RQ: INIT_LIST_HEAD(&cmd->rq->queuelist); @@ -313,13 +313,16 @@ static void null_request_fn(struct request_queue *q) } } -static int null_queue_rq(struct blk_mq_hw_ctx *hctx, struct request *rq) +static int null_queue_rq(struct blk_mq_hw_ctx *hctx, struct request *rq, + bool last) { struct nullb_cmd *cmd = blk_mq_rq_to_pdu(rq); cmd->rq = rq; cmd->nq = hctx->driver_data; + blk_mq_start_request(rq); + null_handle_cmd(cmd); return BLK_MQ_RQ_QUEUE_OK; } @@ -518,6 +521,7 @@ static int null_add_dev(void) nullb->q->queuedata = nullb; queue_flag_set_unlocked(QUEUE_FLAG_NONROT, nullb->q); + queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, nullb->q); disk = nullb->disk = alloc_disk_node(1, home_node); if (!disk) { diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c index 02351e2171651dc47585d4f5a25a9b5e85d303f3..e2bb8afbeae5902081f95c52da9e81e58d1d1693 100644 --- a/drivers/block/nvme-core.c +++ b/drivers/block/nvme-core.c @@ -1916,6 +1916,7 @@ static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, unsigned nsid, ns->queue->queue_flags = QUEUE_FLAG_DEFAULT; queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, ns->queue); queue_flag_set_unlocked(QUEUE_FLAG_NONROT, ns->queue); + queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, ns->queue); blk_queue_make_request(ns->queue, nvme_make_request); ns->dev = dev; ns->queue->queuedata = ns; diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c index fea7e76a00de66e7d20dd6859ad851d8ecb40a35..d48715b287e667bea5205ec75f6e6c40792149a4 100644 --- a/drivers/block/paride/pd.c +++ b/drivers/block/paride/pd.c @@ -722,6 +722,8 @@ static int pd_special_command(struct pd_unit *disk, int err = 0; rq = blk_get_request(disk->gd->queue, READ, __GFP_WAIT); + if (IS_ERR(rq)) + return PTR_ERR(rq); rq->cmd_type = REQ_TYPE_SPECIAL; rq->special = func; diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 758ac442c5b5dd0e8c1d2e2689ac1a0d8c06604d..09e628dafd9d829eadd9abb68c6956d182a582f8 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -704,6 +704,8 @@ static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command * rq = blk_get_request(q, (cgc->data_direction == CGC_DATA_WRITE) ? WRITE : READ, __GFP_WAIT); + if (IS_ERR(rq)) + return PTR_ERR(rq); blk_rq_set_block_pc(rq); if (cgc->buflen) { diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 4b97baf8afa317347ca7ff531f2d8828394084a2..0a54c588e433751f39084459193a5da7700d34db 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -210,6 +210,12 @@ enum obj_request_type { OBJ_REQUEST_NODATA, OBJ_REQUEST_BIO, OBJ_REQUEST_PAGES }; +enum obj_operation_type { + OBJ_OP_WRITE, + OBJ_OP_READ, + OBJ_OP_DISCARD, +}; + enum obj_req_flags { OBJ_REQ_DONE, /* completion flag: not done = 0, done = 1 */ OBJ_REQ_IMG_DATA, /* object usage: standalone = 0, image = 1 */ @@ -276,6 +282,7 @@ enum img_req_flags { IMG_REQ_WRITE, /* I/O direction: read = 0, write = 1 */ IMG_REQ_CHILD, /* initiator: block = 0, child image = 1 */ IMG_REQ_LAYERED, /* ENOENT handling: normal = 0, layered = 1 */ + IMG_REQ_DISCARD, /* discard: normal = 0, discard request = 1 */ }; struct rbd_img_request { @@ -785,6 +792,20 @@ static int parse_rbd_opts_token(char *c, void *private) return 0; } +static char* obj_op_name(enum obj_operation_type op_type) +{ + switch (op_type) { + case OBJ_OP_READ: + return "read"; + case OBJ_OP_WRITE: + return "write"; + case OBJ_OP_DISCARD: + return "discard"; + default: + return "???"; + } +} + /* * Get a ceph client with specific addr and configuration, if one does * not exist create it. Either way, ceph_opts is consumed by this @@ -1600,6 +1621,21 @@ static bool img_request_write_test(struct rbd_img_request *img_request) return test_bit(IMG_REQ_WRITE, &img_request->flags) != 0; } +/* + * Set the discard flag when the img_request is an discard request + */ +static void img_request_discard_set(struct rbd_img_request *img_request) +{ + set_bit(IMG_REQ_DISCARD, &img_request->flags); + smp_mb(); +} + +static bool img_request_discard_test(struct rbd_img_request *img_request) +{ + smp_mb(); + return test_bit(IMG_REQ_DISCARD, &img_request->flags) != 0; +} + static void img_request_child_set(struct rbd_img_request *img_request) { set_bit(IMG_REQ_CHILD, &img_request->flags); @@ -1636,6 +1672,17 @@ static bool img_request_layered_test(struct rbd_img_request *img_request) return test_bit(IMG_REQ_LAYERED, &img_request->flags) != 0; } +static enum obj_operation_type +rbd_img_request_op_type(struct rbd_img_request *img_request) +{ + if (img_request_write_test(img_request)) + return OBJ_OP_WRITE; + else if (img_request_discard_test(img_request)) + return OBJ_OP_DISCARD; + else + return OBJ_OP_READ; +} + static void rbd_img_obj_request_read_callback(struct rbd_obj_request *obj_request) { @@ -1722,6 +1769,21 @@ static void rbd_osd_write_callback(struct rbd_obj_request *obj_request) obj_request_done_set(obj_request); } +static void rbd_osd_discard_callback(struct rbd_obj_request *obj_request) +{ + dout("%s: obj %p result %d %llu\n", __func__, obj_request, + obj_request->result, obj_request->length); + /* + * There is no such thing as a successful short discard. Set + * it to our originally-requested length. + */ + obj_request->xferred = obj_request->length; + /* discarding a non-existent object is not a problem */ + if (obj_request->result == -ENOENT) + obj_request->result = 0; + obj_request_done_set(obj_request); +} + /* * For a simple stat call there's nothing to do. We'll do more if * this is part of a write sequence for a layered image. @@ -1773,6 +1835,11 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req, case CEPH_OSD_OP_STAT: rbd_osd_stat_callback(obj_request); break; + case CEPH_OSD_OP_DELETE: + case CEPH_OSD_OP_TRUNCATE: + case CEPH_OSD_OP_ZERO: + rbd_osd_discard_callback(obj_request); + break; case CEPH_OSD_OP_CALL: case CEPH_OSD_OP_NOTIFY_ACK: case CEPH_OSD_OP_WATCH: @@ -1823,7 +1890,7 @@ static void rbd_osd_req_format_write(struct rbd_obj_request *obj_request) */ static struct ceph_osd_request *rbd_osd_req_create( struct rbd_device *rbd_dev, - bool write_request, + enum obj_operation_type op_type, unsigned int num_ops, struct rbd_obj_request *obj_request) { @@ -1831,16 +1898,18 @@ static struct ceph_osd_request *rbd_osd_req_create( struct ceph_osd_client *osdc; struct ceph_osd_request *osd_req; - if (obj_request_img_data_test(obj_request)) { + if (obj_request_img_data_test(obj_request) && + (op_type == OBJ_OP_DISCARD || op_type == OBJ_OP_WRITE)) { struct rbd_img_request *img_request = obj_request->img_request; - - rbd_assert(write_request == - img_request_write_test(img_request)); - if (write_request) - snapc = img_request->snapc; + if (op_type == OBJ_OP_WRITE) { + rbd_assert(img_request_write_test(img_request)); + } else { + rbd_assert(img_request_discard_test(img_request)); + } + snapc = img_request->snapc; } - rbd_assert(num_ops == 1 || (write_request && num_ops == 2)); + rbd_assert(num_ops == 1 || ((op_type == OBJ_OP_WRITE) && num_ops == 2)); /* Allocate and initialize the request, for the num_ops ops */ @@ -1850,7 +1919,7 @@ static struct ceph_osd_request *rbd_osd_req_create( if (!osd_req) return NULL; /* ENOMEM */ - if (write_request) + if (op_type == OBJ_OP_WRITE || op_type == OBJ_OP_DISCARD) osd_req->r_flags = CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK; else osd_req->r_flags = CEPH_OSD_FLAG_READ; @@ -1865,9 +1934,10 @@ static struct ceph_osd_request *rbd_osd_req_create( } /* - * Create a copyup osd request based on the information in the - * object request supplied. A copyup request has three osd ops, - * a copyup method call, a hint op, and a write op. + * Create a copyup osd request based on the information in the object + * request supplied. A copyup request has two or three osd ops, a + * copyup method call, potentially a hint op, and a write or truncate + * or zero op. */ static struct ceph_osd_request * rbd_osd_req_create_copyup(struct rbd_obj_request *obj_request) @@ -1877,18 +1947,24 @@ rbd_osd_req_create_copyup(struct rbd_obj_request *obj_request) struct rbd_device *rbd_dev; struct ceph_osd_client *osdc; struct ceph_osd_request *osd_req; + int num_osd_ops = 3; rbd_assert(obj_request_img_data_test(obj_request)); img_request = obj_request->img_request; rbd_assert(img_request); - rbd_assert(img_request_write_test(img_request)); + rbd_assert(img_request_write_test(img_request) || + img_request_discard_test(img_request)); + + if (img_request_discard_test(img_request)) + num_osd_ops = 2; - /* Allocate and initialize the request, for the three ops */ + /* Allocate and initialize the request, for all the ops */ snapc = img_request->snapc; rbd_dev = img_request->rbd_dev; osdc = &rbd_dev->rbd_client->client->osdc; - osd_req = ceph_osdc_alloc_request(osdc, snapc, 3, false, GFP_ATOMIC); + osd_req = ceph_osdc_alloc_request(osdc, snapc, num_osd_ops, + false, GFP_ATOMIC); if (!osd_req) return NULL; /* ENOMEM */ @@ -2057,7 +2133,8 @@ static bool rbd_dev_parent_get(struct rbd_device *rbd_dev) static struct rbd_img_request *rbd_img_request_create( struct rbd_device *rbd_dev, u64 offset, u64 length, - bool write_request) + enum obj_operation_type op_type, + struct ceph_snap_context *snapc) { struct rbd_img_request *img_request; @@ -2065,20 +2142,17 @@ static struct rbd_img_request *rbd_img_request_create( if (!img_request) return NULL; - if (write_request) { - down_read(&rbd_dev->header_rwsem); - ceph_get_snap_context(rbd_dev->header.snapc); - up_read(&rbd_dev->header_rwsem); - } - img_request->rq = NULL; img_request->rbd_dev = rbd_dev; img_request->offset = offset; img_request->length = length; img_request->flags = 0; - if (write_request) { + if (op_type == OBJ_OP_DISCARD) { + img_request_discard_set(img_request); + img_request->snapc = snapc; + } else if (op_type == OBJ_OP_WRITE) { img_request_write_set(img_request); - img_request->snapc = rbd_dev->header.snapc; + img_request->snapc = snapc; } else { img_request->snap_id = rbd_dev->spec->snap_id; } @@ -2093,8 +2167,7 @@ static struct rbd_img_request *rbd_img_request_create( kref_init(&img_request->kref); dout("%s: rbd_dev %p %s %llu/%llu -> img %p\n", __func__, rbd_dev, - write_request ? "write" : "read", offset, length, - img_request); + obj_op_name(op_type), offset, length, img_request); return img_request; } @@ -2118,7 +2191,8 @@ static void rbd_img_request_destroy(struct kref *kref) rbd_dev_parent_put(img_request->rbd_dev); } - if (img_request_write_test(img_request)) + if (img_request_write_test(img_request) || + img_request_discard_test(img_request)) ceph_put_snap_context(img_request->snapc); kmem_cache_free(rbd_img_request_cache, img_request); @@ -2134,8 +2208,8 @@ static struct rbd_img_request *rbd_parent_request_create( rbd_assert(obj_request->img_request); rbd_dev = obj_request->img_request->rbd_dev; - parent_request = rbd_img_request_create(rbd_dev->parent, - img_offset, length, false); + parent_request = rbd_img_request_create(rbd_dev->parent, img_offset, + length, OBJ_OP_READ, NULL); if (!parent_request) return NULL; @@ -2176,11 +2250,18 @@ static bool rbd_img_obj_end_request(struct rbd_obj_request *obj_request) result = obj_request->result; if (result) { struct rbd_device *rbd_dev = img_request->rbd_dev; + enum obj_operation_type op_type; + + if (img_request_discard_test(img_request)) + op_type = OBJ_OP_DISCARD; + else if (img_request_write_test(img_request)) + op_type = OBJ_OP_WRITE; + else + op_type = OBJ_OP_READ; rbd_warn(rbd_dev, "%s %llx at %llx (%llx)", - img_request_write_test(img_request) ? "write" : "read", - obj_request->length, obj_request->img_offset, - obj_request->offset); + obj_op_name(op_type), obj_request->length, + obj_request->img_offset, obj_request->offset); rbd_warn(rbd_dev, " result %d xferred %x", result, xferred); if (!img_request->result) @@ -2244,6 +2325,67 @@ static void rbd_img_obj_callback(struct rbd_obj_request *obj_request) rbd_img_request_complete(img_request); } +/* + * Add individual osd ops to the given ceph_osd_request and prepare + * them for submission. num_ops is the current number of + * osd operations already to the object request. + */ +static void rbd_img_obj_request_fill(struct rbd_obj_request *obj_request, + struct ceph_osd_request *osd_request, + enum obj_operation_type op_type, + unsigned int num_ops) +{ + struct rbd_img_request *img_request = obj_request->img_request; + struct rbd_device *rbd_dev = img_request->rbd_dev; + u64 object_size = rbd_obj_bytes(&rbd_dev->header); + u64 offset = obj_request->offset; + u64 length = obj_request->length; + u64 img_end; + u16 opcode; + + if (op_type == OBJ_OP_DISCARD) { + if (!offset && length == object_size && + (!img_request_layered_test(img_request) || + !obj_request_overlaps_parent(obj_request))) { + opcode = CEPH_OSD_OP_DELETE; + } else if ((offset + length == object_size)) { + opcode = CEPH_OSD_OP_TRUNCATE; + } else { + down_read(&rbd_dev->header_rwsem); + img_end = rbd_dev->header.image_size; + up_read(&rbd_dev->header_rwsem); + + if (obj_request->img_offset + length == img_end) + opcode = CEPH_OSD_OP_TRUNCATE; + else + opcode = CEPH_OSD_OP_ZERO; + } + } else if (op_type == OBJ_OP_WRITE) { + opcode = CEPH_OSD_OP_WRITE; + osd_req_op_alloc_hint_init(osd_request, num_ops, + object_size, object_size); + num_ops++; + } else { + opcode = CEPH_OSD_OP_READ; + } + + osd_req_op_extent_init(osd_request, num_ops, opcode, offset, length, + 0, 0); + if (obj_request->type == OBJ_REQUEST_BIO) + osd_req_op_extent_osd_data_bio(osd_request, num_ops, + obj_request->bio_list, length); + else if (obj_request->type == OBJ_REQUEST_PAGES) + osd_req_op_extent_osd_data_pages(osd_request, num_ops, + obj_request->pages, length, + offset & ~PAGE_MASK, false, false); + + /* Discards are also writes */ + if (op_type == OBJ_OP_WRITE || op_type == OBJ_OP_DISCARD) + rbd_osd_req_format_write(obj_request); + else + rbd_osd_req_format_read(obj_request); +} + /* * Split up an image request into one or more object requests, each * to a different object. The "type" parameter indicates whether @@ -2259,28 +2401,26 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request, struct rbd_device *rbd_dev = img_request->rbd_dev; struct rbd_obj_request *obj_request = NULL; struct rbd_obj_request *next_obj_request; - bool write_request = img_request_write_test(img_request); struct bio *bio_list = NULL; unsigned int bio_offset = 0; struct page **pages = NULL; + enum obj_operation_type op_type; u64 img_offset; u64 resid; - u16 opcode; dout("%s: img %p type %d data_desc %p\n", __func__, img_request, (int)type, data_desc); - opcode = write_request ? CEPH_OSD_OP_WRITE : CEPH_OSD_OP_READ; img_offset = img_request->offset; resid = img_request->length; rbd_assert(resid > 0); + op_type = rbd_img_request_op_type(img_request); if (type == OBJ_REQUEST_BIO) { bio_list = data_desc; rbd_assert(img_offset == bio_list->bi_iter.bi_sector << SECTOR_SHIFT); - } else { - rbd_assert(type == OBJ_REQUEST_PAGES); + } else if (type == OBJ_REQUEST_PAGES) { pages = data_desc; } @@ -2289,7 +2429,6 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request, const char *object_name; u64 offset; u64 length; - unsigned int which = 0; object_name = rbd_segment_name(rbd_dev, img_offset); if (!object_name) @@ -2321,7 +2460,7 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request, GFP_ATOMIC); if (!obj_request->bio_list) goto out_unwind; - } else { + } else if (type == OBJ_REQUEST_PAGES) { unsigned int page_count; obj_request->pages = pages; @@ -2332,38 +2471,19 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request, pages += page_count; } - osd_req = rbd_osd_req_create(rbd_dev, write_request, - (write_request ? 2 : 1), - obj_request); + osd_req = rbd_osd_req_create(rbd_dev, op_type, + (op_type == OBJ_OP_WRITE) ? 2 : 1, + obj_request); if (!osd_req) goto out_unwind; + obj_request->osd_req = osd_req; obj_request->callback = rbd_img_obj_callback; - rbd_img_request_get(img_request); - - if (write_request) { - osd_req_op_alloc_hint_init(osd_req, which, - rbd_obj_bytes(&rbd_dev->header), - rbd_obj_bytes(&rbd_dev->header)); - which++; - } - - osd_req_op_extent_init(osd_req, which, opcode, offset, length, - 0, 0); - if (type == OBJ_REQUEST_BIO) - osd_req_op_extent_osd_data_bio(osd_req, which, - obj_request->bio_list, length); - else - osd_req_op_extent_osd_data_pages(osd_req, which, - obj_request->pages, length, - offset & ~PAGE_MASK, false, false); + obj_request->img_offset = img_offset; - if (write_request) - rbd_osd_req_format_write(obj_request); - else - rbd_osd_req_format_read(obj_request); + rbd_img_obj_request_fill(obj_request, osd_req, op_type, 0); - obj_request->img_offset = img_offset; + rbd_img_request_get(img_request); img_offset += length; resid -= length; @@ -2386,7 +2506,8 @@ rbd_img_obj_copyup_callback(struct rbd_obj_request *obj_request) struct page **pages; u32 page_count; - rbd_assert(obj_request->type == OBJ_REQUEST_BIO); + rbd_assert(obj_request->type == OBJ_REQUEST_BIO || + obj_request->type == OBJ_REQUEST_NODATA); rbd_assert(obj_request_img_data_test(obj_request)); img_request = obj_request->img_request; rbd_assert(img_request); @@ -2424,11 +2545,10 @@ rbd_img_obj_parent_read_full_callback(struct rbd_img_request *img_request) struct ceph_osd_client *osdc; struct rbd_device *rbd_dev; struct page **pages; + enum obj_operation_type op_type; u32 page_count; int img_result; u64 parent_length; - u64 offset; - u64 length; rbd_assert(img_request_child_test(img_request)); @@ -2492,26 +2612,10 @@ rbd_img_obj_parent_read_full_callback(struct rbd_img_request *img_request) osd_req_op_cls_request_data_pages(osd_req, 0, pages, parent_length, 0, false, false); - /* Then the hint op */ + /* Add the other op(s) */ - osd_req_op_alloc_hint_init(osd_req, 1, rbd_obj_bytes(&rbd_dev->header), - rbd_obj_bytes(&rbd_dev->header)); - - /* And the original write request op */ - - offset = orig_request->offset; - length = orig_request->length; - osd_req_op_extent_init(osd_req, 2, CEPH_OSD_OP_WRITE, - offset, length, 0, 0); - if (orig_request->type == OBJ_REQUEST_BIO) - osd_req_op_extent_osd_data_bio(osd_req, 2, - orig_request->bio_list, length); - else - osd_req_op_extent_osd_data_pages(osd_req, 2, - orig_request->pages, length, - offset & ~PAGE_MASK, false, false); - - rbd_osd_req_format_write(orig_request); + op_type = rbd_img_request_op_type(orig_request->img_request); + rbd_img_obj_request_fill(orig_request, osd_req, op_type, 1); /* All set, send it off. */ @@ -2728,7 +2832,7 @@ static int rbd_img_obj_exists_submit(struct rbd_obj_request *obj_request) rbd_assert(obj_request->img_request); rbd_dev = obj_request->img_request->rbd_dev; - stat_request->osd_req = rbd_osd_req_create(rbd_dev, false, 1, + stat_request->osd_req = rbd_osd_req_create(rbd_dev, OBJ_OP_READ, 1, stat_request); if (!stat_request->osd_req) goto out; @@ -2748,11 +2852,10 @@ static int rbd_img_obj_exists_submit(struct rbd_obj_request *obj_request) return ret; } -static int rbd_img_obj_request_submit(struct rbd_obj_request *obj_request) +static bool img_obj_request_simple(struct rbd_obj_request *obj_request) { struct rbd_img_request *img_request; struct rbd_device *rbd_dev; - bool known; rbd_assert(obj_request_img_data_test(obj_request)); @@ -2760,22 +2863,44 @@ static int rbd_img_obj_request_submit(struct rbd_obj_request *obj_request) rbd_assert(img_request); rbd_dev = img_request->rbd_dev; + /* Reads */ + if (!img_request_write_test(img_request) && + !img_request_discard_test(img_request)) + return true; + + /* Non-layered writes */ + if (!img_request_layered_test(img_request)) + return true; + + /* + * Layered writes outside of the parent overlap range don't + * share any data with the parent. + */ + if (!obj_request_overlaps_parent(obj_request)) + return true; + /* - * Only writes to layered images need special handling. - * Reads and non-layered writes are simple object requests. - * Layered writes that start beyond the end of the overlap - * with the parent have no parent data, so they too are - * simple object requests. Finally, if the target object is - * known to already exist, its parent data has already been - * copied, so a write to the object can also be handled as a - * simple object request. + * Entire-object layered writes - we will overwrite whatever + * parent data there is anyway. */ - if (!img_request_write_test(img_request) || - !img_request_layered_test(img_request) || - !obj_request_overlaps_parent(obj_request) || - ((known = obj_request_known_test(obj_request)) && - obj_request_exists_test(obj_request))) { + if (!obj_request->offset && + obj_request->length == rbd_obj_bytes(&rbd_dev->header)) + return true; + + /* + * If the object is known to already exist, its parent data has + * already been copied. + */ + if (obj_request_known_test(obj_request) && + obj_request_exists_test(obj_request)) + return true; + + return false; +} +static int rbd_img_obj_request_submit(struct rbd_obj_request *obj_request) +{ + if (img_obj_request_simple(obj_request)) { struct rbd_device *rbd_dev; struct ceph_osd_client *osdc; @@ -2791,7 +2916,7 @@ static int rbd_img_obj_request_submit(struct rbd_obj_request *obj_request) * start by reading the data for the full target object from * the parent so we can use it for a copyup to the target. */ - if (known) + if (obj_request_known_test(obj_request)) return rbd_img_obj_parent_read_full(obj_request); /* We don't know whether the target exists. Go find out. */ @@ -2932,7 +3057,7 @@ static int rbd_obj_notify_ack_sync(struct rbd_device *rbd_dev, u64 notify_id) return -ENOMEM; ret = -ENOMEM; - obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, 1, + obj_request->osd_req = rbd_osd_req_create(rbd_dev, OBJ_OP_READ, 1, obj_request); if (!obj_request->osd_req) goto out; @@ -2995,7 +3120,7 @@ static struct rbd_obj_request *rbd_obj_watch_request_helper( if (!obj_request) return ERR_PTR(-ENOMEM); - obj_request->osd_req = rbd_osd_req_create(rbd_dev, true, 1, + obj_request->osd_req = rbd_osd_req_create(rbd_dev, OBJ_OP_WRITE, 1, obj_request); if (!obj_request->osd_req) { ret = -ENOMEM; @@ -3133,7 +3258,7 @@ static int rbd_obj_method_sync(struct rbd_device *rbd_dev, obj_request->pages = pages; obj_request->page_count = page_count; - obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, 1, + obj_request->osd_req = rbd_osd_req_create(rbd_dev, OBJ_OP_READ, 1, obj_request); if (!obj_request->osd_req) goto out; @@ -3183,11 +3308,20 @@ static int rbd_obj_method_sync(struct rbd_device *rbd_dev, static void rbd_handle_request(struct rbd_device *rbd_dev, struct request *rq) { struct rbd_img_request *img_request; + struct ceph_snap_context *snapc = NULL; u64 offset = (u64)blk_rq_pos(rq) << SECTOR_SHIFT; u64 length = blk_rq_bytes(rq); - bool wr = rq_data_dir(rq) == WRITE; + enum obj_operation_type op_type; + u64 mapping_size; int result; + if (rq->cmd_flags & REQ_DISCARD) + op_type = OBJ_OP_DISCARD; + else if (rq->cmd_flags & REQ_WRITE) + op_type = OBJ_OP_WRITE; + else + op_type = OBJ_OP_READ; + /* Ignore/skip any zero-length requests */ if (!length) { @@ -3196,9 +3330,9 @@ static void rbd_handle_request(struct rbd_device *rbd_dev, struct request *rq) goto err_rq; } - /* Disallow writes to a read-only device */ + /* Only reads are allowed to a read-only device */ - if (wr) { + if (op_type != OBJ_OP_READ) { if (rbd_dev->mapping.read_only) { result = -EROFS; goto err_rq; @@ -3226,21 +3360,35 @@ static void rbd_handle_request(struct rbd_device *rbd_dev, struct request *rq) goto err_rq; /* Shouldn't happen */ } - if (offset + length > rbd_dev->mapping.size) { + down_read(&rbd_dev->header_rwsem); + mapping_size = rbd_dev->mapping.size; + if (op_type != OBJ_OP_READ) { + snapc = rbd_dev->header.snapc; + ceph_get_snap_context(snapc); + } + up_read(&rbd_dev->header_rwsem); + + if (offset + length > mapping_size) { rbd_warn(rbd_dev, "beyond EOD (%llu~%llu > %llu)", offset, - length, rbd_dev->mapping.size); + length, mapping_size); result = -EIO; goto err_rq; } - img_request = rbd_img_request_create(rbd_dev, offset, length, wr); + img_request = rbd_img_request_create(rbd_dev, offset, length, op_type, + snapc); if (!img_request) { result = -ENOMEM; goto err_rq; } img_request->rq = rq; - result = rbd_img_request_fill(img_request, OBJ_REQUEST_BIO, rq->bio); + if (op_type == OBJ_OP_DISCARD) + result = rbd_img_request_fill(img_request, OBJ_REQUEST_NODATA, + NULL); + else + result = rbd_img_request_fill(img_request, OBJ_REQUEST_BIO, + rq->bio); if (result) goto err_img_request; @@ -3255,7 +3403,9 @@ static void rbd_handle_request(struct rbd_device *rbd_dev, struct request *rq) err_rq: if (result) rbd_warn(rbd_dev, "%s %llx at %llx result %d", - wr ? "write" : "read", length, offset, result); + obj_op_name(op_type), length, offset, result); + if (snapc) + ceph_put_snap_context(snapc); blk_end_request_all(rq, result); } @@ -3393,7 +3543,7 @@ static int rbd_obj_read_sync(struct rbd_device *rbd_dev, obj_request->pages = pages; obj_request->page_count = page_count; - obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, 1, + obj_request->osd_req = rbd_osd_req_create(rbd_dev, OBJ_OP_READ, 1, obj_request); if (!obj_request->osd_req) goto out; @@ -3610,6 +3760,13 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) blk_queue_io_min(q, segment_size); blk_queue_io_opt(q, segment_size); + /* enable the discard support */ + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q); + q->limits.discard_granularity = segment_size; + q->limits.discard_alignment = segment_size; + q->limits.max_discard_sectors = segment_size / SECTOR_SIZE; + q->limits.discard_zeroes_data = 1; + blk_queue_merge_bvec(q, rbd_merge_bvec); disk->queue = q; @@ -4924,7 +5081,7 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev) ret = image_id ? 0 : -ENOMEM; if (!ret) rbd_dev->image_format = 1; - } else if (ret > sizeof (__le32)) { + } else if (ret >= 0) { void *p = response; image_id = ceph_extract_encoded_string(&p, p + ret, @@ -4932,8 +5089,6 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev) ret = PTR_ERR_OR_ZERO(image_id); if (!ret) rbd_dev->image_format = 2; - } else { - ret = -EINVAL; } if (!ret) { @@ -5087,7 +5242,8 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev) set_capacity(rbd_dev->disk, rbd_dev->mapping.size / SECTOR_SIZE); set_disk_ro(rbd_dev->disk, rbd_dev->mapping.read_only); - rbd_dev->rq_wq = alloc_workqueue("%s", 0, 0, rbd_dev->disk->disk_name); + rbd_dev->rq_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, + rbd_dev->disk->disk_name); if (!rbd_dev->rq_wq) { ret = -ENOMEM; goto err_out_mapping; diff --git a/drivers/block/rsxx/core.c b/drivers/block/rsxx/core.c index 3265ce94d2826e05c8ee2bb2e245eb34f55db41b..d8b2488aaade109847a3b9156e1279b112907c14 100644 --- a/drivers/block/rsxx/core.c +++ b/drivers/block/rsxx/core.c @@ -837,7 +837,7 @@ static int rsxx_pci_probe(struct pci_dev *dev, "Failed to enable MSI\n"); } - st = request_irq(dev->irq, rsxx_isr, IRQF_DISABLED | IRQF_SHARED, + st = request_irq(dev->irq, rsxx_isr, IRQF_SHARED, DRIVER_NAME, card); if (st) { dev_err(CARD_TO_DEV(card), diff --git a/drivers/block/rsxx/dev.c b/drivers/block/rsxx/dev.c index 2839d37e5af77922051cb1d48561602ea3a3c2b7..40ee7705df63a061863539a03b8df5555f799c93 100644 --- a/drivers/block/rsxx/dev.c +++ b/drivers/block/rsxx/dev.c @@ -307,6 +307,7 @@ int rsxx_setup_dev(struct rsxx_cardinfo *card) blk_queue_physical_block_size(card->queue, RSXX_HW_BLK_SIZE); queue_flag_set_unlocked(QUEUE_FLAG_NONROT, card->queue); + queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, card->queue); if (rsxx_discard_supported(card)) { queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, card->queue); blk_queue_max_discard_sectors(card->queue, diff --git a/drivers/block/skd_main.c b/drivers/block/skd_main.c index 8fcdcfb4b47238469022146fe7b1861c904b7a2f..1e46eb2305c04a8e63e378befb95a905fdf01850 100644 --- a/drivers/block/skd_main.c +++ b/drivers/block/skd_main.c @@ -4426,6 +4426,7 @@ static int skd_cons_disk(struct skd_device *skdev) q->limits.discard_zeroes_data = 1; queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q); queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q); + queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, q); spin_lock_irqsave(&skdev->lock, flags); pr_debug("%s:%s:%d stopping %s queue\n", diff --git a/drivers/block/sx8.c b/drivers/block/sx8.c index d5e2d12b9d9e329d77fb21560b8f21e5e98a11f4..5d552857de412e32864ba4378a2313014c7b3564 100644 --- a/drivers/block/sx8.c +++ b/drivers/block/sx8.c @@ -568,7 +568,7 @@ static struct carm_request *carm_get_special(struct carm_host *host) return NULL; rq = blk_get_request(host->oob_q, WRITE /* bogus */, GFP_KERNEL); - if (!rq) { + if (IS_ERR(rq)) { spin_lock_irqsave(&host->lock, flags); carm_put_request(host, crq); spin_unlock_irqrestore(&host->lock, flags); diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 0a581400de0f4006e95de9e0f289d382608eb517..c6a27d54ad620b5ea25ae674d6b9a358b0f312fa 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -41,12 +41,6 @@ struct virtio_blk /* Process context for config space updates */ struct work_struct config_work; - /* Lock for config space updates */ - struct mutex config_lock; - - /* enable config space updates */ - bool config_enable; - /* What host tells us, plus 2 for header & tailer. */ unsigned int sg_elems; @@ -135,7 +129,7 @@ static inline void virtblk_request_done(struct request *req) req->errors = (error != 0); } - blk_mq_end_io(req, error); + blk_mq_end_request(req, error); } static void virtblk_done(struct virtqueue *vq) @@ -164,14 +158,14 @@ static void virtblk_done(struct virtqueue *vq) spin_unlock_irqrestore(&vblk->vqs[qid].lock, flags); } -static int virtio_queue_rq(struct blk_mq_hw_ctx *hctx, struct request *req) +static int virtio_queue_rq(struct blk_mq_hw_ctx *hctx, struct request *req, + bool last) { struct virtio_blk *vblk = hctx->queue->queuedata; struct virtblk_req *vbr = blk_mq_rq_to_pdu(req); unsigned long flags; unsigned int num; int qid = hctx->queue_num; - const bool last = (req->cmd_flags & REQ_END) != 0; int err; bool notify = false; @@ -205,6 +199,8 @@ static int virtio_queue_rq(struct blk_mq_hw_ctx *hctx, struct request *req) } } + blk_mq_start_request(req); + num = blk_rq_map_sg(hctx->queue, vbr->req, vbr->sg); if (num) { if (rq_data_dir(vbr->req) == WRITE) @@ -347,10 +343,6 @@ static void virtblk_config_changed_work(struct work_struct *work) char *envp[] = { "RESIZE=1", NULL }; u64 capacity, size; - mutex_lock(&vblk->config_lock); - if (!vblk->config_enable) - goto done; - /* Host must always specify the capacity. */ virtio_cread(vdev, struct virtio_blk_config, capacity, &capacity); @@ -374,8 +366,6 @@ static void virtblk_config_changed_work(struct work_struct *work) set_capacity(vblk->disk, capacity); revalidate_disk(vblk->disk); kobject_uevent_env(&disk_to_dev(vblk->disk)->kobj, KOBJ_CHANGE, envp); -done: - mutex_unlock(&vblk->config_lock); } static void virtblk_config_changed(struct virtio_device *vdev) @@ -606,10 +596,8 @@ static int virtblk_probe(struct virtio_device *vdev) vblk->vdev = vdev; vblk->sg_elems = sg_elems; - mutex_init(&vblk->config_lock); INIT_WORK(&vblk->config_work, virtblk_config_changed_work); - vblk->config_enable = true; err = init_vq(vblk); if (err) @@ -733,6 +721,8 @@ static int virtblk_probe(struct virtio_device *vdev) if (!err && opt_io_size) blk_queue_io_opt(q, blk_size * opt_io_size); + virtio_device_ready(vdev); + add_disk(vblk->disk); err = device_create_file(disk_to_dev(vblk->disk), &dev_attr_serial); if (err) @@ -771,10 +761,8 @@ static void virtblk_remove(struct virtio_device *vdev) int index = vblk->index; int refc; - /* Prevent config work handler from accessing the device. */ - mutex_lock(&vblk->config_lock); - vblk->config_enable = false; - mutex_unlock(&vblk->config_lock); + /* Make sure no work handler is accessing the device. */ + flush_work(&vblk->config_work); del_gendisk(vblk->disk); blk_cleanup_queue(vblk->disk->queue); @@ -784,8 +772,6 @@ static void virtblk_remove(struct virtio_device *vdev) /* Stop all the virtqueues. */ vdev->config->reset(vdev); - flush_work(&vblk->config_work); - refc = atomic_read(&disk_to_dev(vblk->disk)->kobj.kref.refcount); put_disk(vblk->disk); vdev->config->del_vqs(vdev); @@ -805,11 +791,7 @@ static int virtblk_freeze(struct virtio_device *vdev) /* Ensure we don't receive any more interrupts */ vdev->config->reset(vdev); - /* Prevent config work handler from accessing the device. */ - mutex_lock(&vblk->config_lock); - vblk->config_enable = false; - mutex_unlock(&vblk->config_lock); - + /* Make sure no work handler is accessing the device. */ flush_work(&vblk->config_work); blk_mq_stop_hw_queues(vblk->disk->queue); @@ -823,12 +805,14 @@ static int virtblk_restore(struct virtio_device *vdev) struct virtio_blk *vblk = vdev->priv; int ret; - vblk->config_enable = true; ret = init_vq(vdev->priv); - if (!ret) - blk_mq_start_stopped_hw_queues(vblk->disk->queue, true); + if (ret) + return ret; + + virtio_device_ready(vdev); - return ret; + blk_mq_start_stopped_hw_queues(vblk->disk->queue, true); + return 0; } #endif diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index 64c60edcdfbc5546cd84538986b5d26625f9f248..63fc7f06a0146821296e2d4c625f680d6c9ac008 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -763,6 +763,7 @@ static int xen_blkbk_map(struct xen_blkif *blkif, BUG_ON(new_map_idx >= segs_to_map); if (unlikely(map[new_map_idx].status != 0)) { pr_debug(DRV_PFX "invalid buffer -- could not remap it\n"); + put_free_pages(blkif, &pages[seg_idx]->page, 1); pages[seg_idx]->handle = BLKBACK_INVALID_HANDLE; ret |= 1; goto next; diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c index 0b13b1c9a01e397fbb557e676beb77742c9737bc..630a489e757d61eb9fdb2b9f928ac77898a8dd48 100644 --- a/drivers/block/xen-blkback/xenbus.c +++ b/drivers/block/xen-blkback/xenbus.c @@ -270,6 +270,9 @@ static int xen_blkif_disconnect(struct xen_blkif *blkif) blkif->blk_rings.common.sring = NULL; } + /* Remove all persistent grants and the cache of ballooned pages. */ + xen_blkbk_free_caches(blkif); + return 0; } @@ -281,9 +284,6 @@ static void xen_blkif_free(struct xen_blkif *blkif) xen_blkif_disconnect(blkif); xen_vbd_free(&blkif->vbd); - /* Remove all persistent grants and the cache of ballooned pages. */ - xen_blkbk_free_caches(blkif); - /* Make sure everything is drained before shutting down */ BUG_ON(blkif->persistent_gnt_c != 0); BUG_ON(atomic_read(&blkif->persistent_gnt_in_use) != 0); diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 37af03e9d859d113f28c8ccb2be70be98a5ce831..5ac312f6e0be6c136f78ea46186de774fb4aae3b 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -582,6 +582,14 @@ static inline void flush_requests(struct blkfront_info *info) notify_remote_via_irq(info->irq); } +static inline bool blkif_request_flush_valid(struct request *req, + struct blkfront_info *info) +{ + return ((req->cmd_type != REQ_TYPE_FS) || + ((req->cmd_flags & (REQ_FLUSH | REQ_FUA)) && + !info->flush_op)); +} + /* * do_blkif_request * read a block; request is in a request queue @@ -604,9 +612,7 @@ static void do_blkif_request(struct request_queue *rq) blk_start_request(req); - if ((req->cmd_type != REQ_TYPE_FS) || - ((req->cmd_flags & (REQ_FLUSH | REQ_FUA)) && - !info->flush_op)) { + if (blkif_request_flush_valid(req, info)) { __blk_end_request_all(req, -EIO); continue; } diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 3b850164c65c5c97cdd9fb9473031582effa74fc..0e63e8aa8279a7993344f15d0d2f48b2536b497a 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -1031,6 +1031,7 @@ static int create_device(struct zram *zram, int device_id) set_capacity(zram->disk, 0); /* zram devices sort of resembles non-rotational disks */ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue); + queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, zram->disk->queue); /* * To ensure that we always get PAGE_SIZE aligned * and n*PAGE_SIZED sized I/O requests. diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 898b84bba28a88b3b61e7cfda3e43f2f7fabb16a..5d28a45d2960c6a40945b134c755ff03a1117c76 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -2180,8 +2180,8 @@ static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf, len = nr * CD_FRAMESIZE_RAW; rq = blk_get_request(q, READ, GFP_KERNEL); - if (!rq) { - ret = -ENOMEM; + if (IS_ERR(rq)) { + ret = PTR_ERR(rq); break; } blk_rq_set_block_pc(rq); diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 132c9ccfdc62b382cb22a9870e44038c6495a39d..72295ea2fd1c9b99e0be51fd352e99c1de224a86 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -109,8 +109,8 @@ static int probe_common(struct virtio_device *vdev) vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL); if (index < 0) { - kfree(vi); - return index; + err = index; + goto err_ida; } sprintf(vi->name, "virtio_rng.%d", index); init_completion(&vi->have_data); @@ -128,13 +128,16 @@ static int probe_common(struct virtio_device *vdev) vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input"); if (IS_ERR(vi->vq)) { err = PTR_ERR(vi->vq); - vi->vq = NULL; - kfree(vi); - ida_simple_remove(&rng_index_ida, index); - return err; + goto err_find; } return 0; + +err_find: + ida_simple_remove(&rng_index_ida, index); +err_ida: + kfree(vi); + return err; } static void remove_common(struct virtio_device *vdev) diff --git a/drivers/char/random.c b/drivers/char/random.c index c18d41db83d8685b005cd5c409a26b7d032e311f..82759cef904332cbe6800d4be9c4e08aa420f19c 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -874,7 +874,7 @@ static __u32 get_reg(struct fast_pool *f, struct pt_regs *regs) void add_interrupt_randomness(int irq, int irq_flags) { struct entropy_store *r; - struct fast_pool *fast_pool = &__get_cpu_var(irq_randomness); + struct fast_pool *fast_pool = this_cpu_ptr(&irq_randomness); struct pt_regs *regs = get_irq_regs(); unsigned long now = jiffies; cycles_t cycles = random_get_entropy(); diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index b585b4789822f976dc794bbfc1923a4215c82d11..bfa640023e64893251d2cb2cf54af5a853d89568 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1449,6 +1449,8 @@ static int add_port(struct ports_device *portdev, u32 id) spin_lock_init(&port->outvq_lock); init_waitqueue_head(&port->waitqueue); + virtio_device_ready(portdev->vdev); + /* Fill the in_vq with buffers so the host can send us data. */ nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock); if (!nr_added_bufs) { @@ -2182,6 +2184,8 @@ static int virtcons_restore(struct virtio_device *vdev) if (ret) return ret; + virtio_device_ready(portdev->vdev); + if (use_multiport(portdev)) fill_queue(portdev->c_ivq, &portdev->c_ivq_lock); diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index cfd3af7b2cbd16cec12c23cf0767cc787c66a812..455fd17d938eaa6525f4af01e0d0d2f786bd08d8 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -32,12 +32,32 @@ config COMMON_CLK_WM831X source "drivers/clk/versatile/Kconfig" +config COMMON_CLK_MAX_GEN + bool + config COMMON_CLK_MAX77686 tristate "Clock driver for Maxim 77686 MFD" depends on MFD_MAX77686 + select COMMON_CLK_MAX_GEN ---help--- This driver supports Maxim 77686 crystal oscillator clock. +config COMMON_CLK_MAX77802 + tristate "Clock driver for Maxim 77802 PMIC" + depends on MFD_MAX77686 + select COMMON_CLK_MAX_GEN + ---help--- + This driver supports Maxim 77802 crystal oscillator clock. + +config COMMON_CLK_RK808 + tristate "Clock driver for RK808" + depends on MFD_RK808 + ---help--- + This driver supports RK808 crystal oscillator clock. These + multi-function devices have two fixed-rate oscillators, + clocked at 32KHz each. Clkout1 is always on, Clkout2 can off + by control register. + config COMMON_CLK_SI5351 tristate "Clock driver for SiLabs 5351A/B/C" depends on I2C @@ -109,6 +129,11 @@ config COMMON_CLK_PALMAS This driver supports TI Palmas devices 32KHz output KG and KG_AUDIO using common clock framework. +config COMMON_CLK_PXA + def_bool COMMON_CLK && ARCH_PXA + ---help--- + Sypport for the Marvell PXA SoC. + source "drivers/clk/qcom/Kconfig" endmenu diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index f537a0b1f798159824aa1d90ffd4378d1bff908d..d5fba5bc6e1bc1f07991be58f367c30a253a58f7 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-gate.o obj-$(CONFIG_COMMON_CLK) += clk-mux.o obj-$(CONFIG_COMMON_CLK) += clk-composite.o obj-$(CONFIG_COMMON_CLK) += clk-fractional-divider.o +obj-$(CONFIG_COMMON_CLK) += clk-gpio-gate.o ifeq ($(CONFIG_OF), y) obj-$(CONFIG_COMMON_CLK) += clk-conf.o endif @@ -22,12 +23,15 @@ obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o +obj-$(CONFIG_COMMON_CLK_MAX_GEN) += clk-max-gen.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o +obj-$(CONFIG_COMMON_CLK_MAX77802) += clk-max77802.o obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o +obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o @@ -48,6 +52,7 @@ obj-$(CONFIG_ARCH_MMP) += mmp/ endif obj-$(CONFIG_PLAT_ORION) += mvebu/ obj-$(CONFIG_ARCH_MXS) += mxs/ +obj-$(CONFIG_COMMON_CLK_PXA) += pxa/ obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/ diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c index cf6ed023504cbc4afbf98c523b7e48e09fab0d56..6ec79dbc0840ad8940e9e9ab599a0f865f1cd881 100644 --- a/drivers/clk/at91/clk-pll.c +++ b/drivers/clk/at91/clk-pll.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -29,9 +30,12 @@ #define PLL_DIV(reg) ((reg) & PLL_DIV_MASK) #define PLL_MUL(reg, layout) (((reg) >> (layout)->mul_shift) & \ (layout)->mul_mask) +#define PLL_MUL_MIN 2 +#define PLL_MUL_MASK(layout) ((layout)->mul_mask) +#define PLL_MUL_MAX(layout) (PLL_MUL_MASK(layout) + 1) #define PLL_ICPR_SHIFT(id) ((id) * 16) #define PLL_ICPR_MASK(id) (0xffff << PLL_ICPR_SHIFT(id)) -#define PLL_MAX_COUNT 0x3ff +#define PLL_MAX_COUNT 0x3f #define PLL_COUNT_SHIFT 8 #define PLL_OUT_SHIFT 14 #define PLL_MAX_ID 1 @@ -147,115 +151,113 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_pll *pll = to_clk_pll(hw); - const struct clk_pll_layout *layout = pll->layout; - struct at91_pmc *pmc = pll->pmc; - int offset = PLL_REG(pll->id); - u32 tmp = pmc_read(pmc, offset) & layout->pllr_mask; - u8 div = PLL_DIV(tmp); - u16 mul = PLL_MUL(tmp, layout); - if (!div || !mul) + + if (!pll->div || !pll->mul) return 0; - return (parent_rate * (mul + 1)) / div; + return (parent_rate / pll->div) * (pll->mul + 1); } static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate, unsigned long parent_rate, u32 *div, u32 *mul, u32 *index) { - unsigned long maxrate; - unsigned long minrate; - unsigned long divrate; - unsigned long bestdiv = 1; - unsigned long bestmul; - unsigned long tmpdiv; - unsigned long roundup; - unsigned long rounddown; - unsigned long remainder; - unsigned long bestremainder; - unsigned long maxmul; - unsigned long maxdiv; - unsigned long mindiv; - int i = 0; const struct clk_pll_layout *layout = pll->layout; const struct clk_pll_characteristics *characteristics = pll->characteristics; + unsigned long bestremainder = ULONG_MAX; + unsigned long maxdiv, mindiv, tmpdiv; + long bestrate = -ERANGE; + unsigned long bestdiv; + unsigned long bestmul; + int i = 0; - /* Minimum divider = 1 */ - /* Maximum multiplier = max_mul */ - maxmul = layout->mul_mask + 1; - maxrate = (parent_rate * maxmul) / 1; - - /* Maximum divider = max_div */ - /* Minimum multiplier = 2 */ - maxdiv = PLL_DIV_MAX; - minrate = (parent_rate * 2) / maxdiv; - + /* Check if parent_rate is a valid input rate */ if (parent_rate < characteristics->input.min || - parent_rate < characteristics->input.max) - return -ERANGE; - - if (parent_rate < minrate || parent_rate > maxrate) + parent_rate > characteristics->input.max) return -ERANGE; - for (i = 0; i < characteristics->num_output; i++) { - if (parent_rate >= characteristics->output[i].min && - parent_rate <= characteristics->output[i].max) - break; - } - - if (i >= characteristics->num_output) - return -ERANGE; - - bestmul = rate / parent_rate; - rounddown = parent_rate % rate; - roundup = rate - rounddown; - bestremainder = roundup < rounddown ? roundup : rounddown; - - if (!bestremainder) { - if (div) - *div = bestdiv; - if (mul) - *mul = bestmul; - if (index) - *index = i; - return rate; - } - - maxdiv = 255 / (bestmul + 1); - if (parent_rate / maxdiv < characteristics->input.min) - maxdiv = parent_rate / characteristics->input.min; - mindiv = parent_rate / characteristics->input.max; - if (parent_rate % characteristics->input.max) - mindiv++; - - for (tmpdiv = mindiv; tmpdiv < maxdiv; tmpdiv++) { - divrate = parent_rate / tmpdiv; - - rounddown = rate % divrate; - roundup = divrate - rounddown; - remainder = roundup < rounddown ? roundup : rounddown; - + /* + * Calculate minimum divider based on the minimum multiplier, the + * parent_rate and the requested rate. + * Should always be 2 according to the input and output characteristics + * of the PLL blocks. + */ + mindiv = (parent_rate * PLL_MUL_MIN) / rate; + if (!mindiv) + mindiv = 1; + + /* + * Calculate the maximum divider which is limited by PLL register + * layout (limited by the MUL or DIV field size). + */ + maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX(layout), rate); + if (maxdiv > PLL_DIV_MAX) + maxdiv = PLL_DIV_MAX; + + /* + * Iterate over the acceptable divider values to find the best + * divider/multiplier pair (the one that generates the closest + * rate to the requested one). + */ + for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) { + unsigned long remainder; + unsigned long tmprate; + unsigned long tmpmul; + + /* + * Calculate the multiplier associated with the current + * divider that provide the closest rate to the requested one. + */ + tmpmul = DIV_ROUND_CLOSEST(rate, parent_rate / tmpdiv); + tmprate = (parent_rate / tmpdiv) * tmpmul; + if (tmprate > rate) + remainder = tmprate - rate; + else + remainder = rate - tmprate; + + /* + * Compare the remainder with the best remainder found until + * now and elect a new best multiplier/divider pair if the + * current remainder is smaller than the best one. + */ if (remainder < bestremainder) { bestremainder = remainder; - bestmul = rate / divrate; bestdiv = tmpdiv; + bestmul = tmpmul; + bestrate = tmprate; } + /* + * We've found a perfect match! + * Stop searching now and use this multiplier/divider pair. + */ if (!remainder) break; } - rate = (parent_rate / bestdiv) * bestmul; + /* We haven't found any multiplier/divider pair => return -ERANGE */ + if (bestrate < 0) + return bestrate; + + /* Check if bestrate is a valid output rate */ + for (i = 0; i < characteristics->num_output; i++) { + if (bestrate >= characteristics->output[i].min && + bestrate <= characteristics->output[i].max) + break; + } + + if (i >= characteristics->num_output) + return -ERANGE; if (div) *div = bestdiv; if (mul) - *mul = bestmul; + *mul = bestmul - 1; if (index) *index = i; - return rate; + return bestrate; } static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c index 7d1d26a4bd04459bb509a7004fd0043476b70ff9..24b5b020753a9e4a66a5d3db7c8f7ad8bee0b928 100644 --- a/drivers/clk/at91/clk-usb.c +++ b/drivers/clk/at91/clk-usb.c @@ -238,16 +238,22 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); + struct clk *parent = __clk_get_parent(hw->clk); unsigned long bestrate = 0; int bestdiff = -1; unsigned long tmprate; int tmpdiff; int i = 0; - for (i = 0; i < 4; i++) { + for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) { + unsigned long tmp_parent_rate; + if (!usb->divisors[i]) continue; - tmprate = *parent_rate / usb->divisors[i]; + + tmp_parent_rate = rate * usb->divisors[i]; + tmp_parent_rate = __clk_round_rate(parent, tmp_parent_rate); + tmprate = tmp_parent_rate / usb->divisors[i]; if (tmprate < rate) tmpdiff = rate - tmprate; else @@ -256,6 +262,7 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, if (bestdiff < 0 || bestdiff > tmpdiff) { bestrate = tmprate; bestdiff = tmpdiff; + *parent_rate = tmp_parent_rate; } if (!bestdiff) @@ -272,10 +279,13 @@ static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate, int i; struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw); struct at91_pmc *pmc = usb->pmc; - unsigned long div = parent_rate / rate; + unsigned long div; - if (parent_rate % rate) + if (!rate || parent_rate % rate) return -EINVAL; + + div = parent_rate / rate; + for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) { if (usb->divisors[i] == div) { tmp = pmc_read(pmc, AT91_CKGR_PLLBR) & @@ -311,7 +321,7 @@ at91rm9200_clk_register_usb(struct at91_pmc *pmc, const char *name, init.ops = &at91rm9200_usb_ops; init.parent_names = &parent_name; init.num_parents = 1; - init.flags = 0; + init.flags = CLK_SET_RATE_PARENT; usb->hw.init = &init; usb->pmc = pmc; diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c index 1127ee46b8024548151b9920246ef286cc887b99..e619285c6defddc731ce4eac34c3b5527bf5e47b 100644 --- a/drivers/clk/clk-axi-clkgen.c +++ b/drivers/clk/clk-axi-clkgen.c @@ -544,7 +544,6 @@ static int axi_clkgen_remove(struct platform_device *pdev) static struct platform_driver axi_clkgen_driver = { .driver = { .name = "adi-axi-clkgen", - .owner = THIS_MODULE, .of_match_table = axi_clkgen_ids, }, .probe = axi_clkgen_probe, diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c index ede685ca0d20b03ef5b9b6e6bd37c4d57e0db625..82a59d0086cc79d654c4ceaf4aa42a8491467dbc 100644 --- a/drivers/clk/clk-fractional-divider.c +++ b/drivers/clk/clk-fractional-divider.c @@ -36,7 +36,7 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw, m = (val & fd->mmask) >> fd->mshift; n = (val & fd->nmask) >> fd->nshift; - ret = parent_rate * m; + ret = (u64)parent_rate * m; do_div(ret, n); return ret; diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 4a58c55255bd884f3f1e7deea52048712b297729..51fd87fb7ba691e8a52e40ddc1ee4524ecd3e781 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -45,7 +45,7 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable) { struct clk_gate *gate = to_clk_gate(hw); int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; - unsigned long flags = 0; + unsigned long uninitialized_var(flags); u32 reg; set ^= enable; diff --git a/drivers/clk/clk-gpio-gate.c b/drivers/clk/clk-gpio-gate.c new file mode 100644 index 0000000000000000000000000000000000000000..08e43224fd522a876ed8cf736e43f2bc739c930d --- /dev/null +++ b/drivers/clk/clk-gpio-gate.c @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2013 - 2014 Texas Instruments Incorporated - http://www.ti.com + * Author: Jyri Sarha + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Gpio gated clock implementation + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * DOC: basic gpio gated clock which can be enabled and disabled + * with gpio output + * Traits of this clock: + * prepare - clk_(un)prepare only ensures parent is (un)prepared + * enable - clk_enable and clk_disable are functional & control gpio + * rate - inherits rate from parent. No clk_set_rate support + * parent - fixed parent. No clk_set_parent support + */ + +#define to_clk_gpio(_hw) container_of(_hw, struct clk_gpio, hw) + +static int clk_gpio_gate_enable(struct clk_hw *hw) +{ + struct clk_gpio *clk = to_clk_gpio(hw); + + gpiod_set_value(clk->gpiod, 1); + + return 0; +} + +static void clk_gpio_gate_disable(struct clk_hw *hw) +{ + struct clk_gpio *clk = to_clk_gpio(hw); + + gpiod_set_value(clk->gpiod, 0); +} + +static int clk_gpio_gate_is_enabled(struct clk_hw *hw) +{ + struct clk_gpio *clk = to_clk_gpio(hw); + + return gpiod_get_value(clk->gpiod); +} + +const struct clk_ops clk_gpio_gate_ops = { + .enable = clk_gpio_gate_enable, + .disable = clk_gpio_gate_disable, + .is_enabled = clk_gpio_gate_is_enabled, +}; +EXPORT_SYMBOL_GPL(clk_gpio_gate_ops); + +/** + * clk_register_gpio - register a gpip clock with the clock framework + * @dev: device that is registering this clock + * @name: name of this clock + * @parent_name: name of this clock's parent + * @gpiod: gpio descriptor to gate this clock + */ +struct clk *clk_register_gpio_gate(struct device *dev, const char *name, + const char *parent_name, struct gpio_desc *gpiod, + unsigned long flags) +{ + struct clk_gpio *clk_gpio = NULL; + struct clk *clk = ERR_PTR(-EINVAL); + struct clk_init_data init = { NULL }; + unsigned long gpio_flags; + int err; + + if (gpiod_is_active_low(gpiod)) + gpio_flags = GPIOF_OUT_INIT_HIGH; + else + gpio_flags = GPIOF_OUT_INIT_LOW; + + if (dev) + err = devm_gpio_request_one(dev, desc_to_gpio(gpiod), + gpio_flags, name); + else + err = gpio_request_one(desc_to_gpio(gpiod), gpio_flags, name); + + if (err) { + pr_err("%s: %s: Error requesting clock control gpio %u\n", + __func__, name, desc_to_gpio(gpiod)); + return ERR_PTR(err); + } + + if (dev) + clk_gpio = devm_kzalloc(dev, sizeof(struct clk_gpio), + GFP_KERNEL); + else + clk_gpio = kzalloc(sizeof(struct clk_gpio), GFP_KERNEL); + + if (!clk_gpio) { + clk = ERR_PTR(-ENOMEM); + goto clk_register_gpio_gate_err; + } + + init.name = name; + init.ops = &clk_gpio_gate_ops; + init.flags = flags | CLK_IS_BASIC; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + clk_gpio->gpiod = gpiod; + clk_gpio->hw.init = &init; + + clk = clk_register(dev, &clk_gpio->hw); + + if (!IS_ERR(clk)) + return clk; + + if (!dev) + kfree(clk_gpio); + +clk_register_gpio_gate_err: + gpiod_put(gpiod); + + return clk; +} +EXPORT_SYMBOL_GPL(clk_register_gpio_gate); + +#ifdef CONFIG_OF +/** + * The clk_register_gpio_gate has to be delayed, because the EPROBE_DEFER + * can not be handled properly at of_clk_init() call time. + */ + +struct clk_gpio_gate_delayed_register_data { + struct device_node *node; + struct mutex lock; + struct clk *clk; +}; + +static struct clk *of_clk_gpio_gate_delayed_register_get( + struct of_phandle_args *clkspec, + void *_data) +{ + struct clk_gpio_gate_delayed_register_data *data = _data; + struct clk *clk; + const char *clk_name = data->node->name; + const char *parent_name; + struct gpio_desc *gpiod; + int gpio; + + mutex_lock(&data->lock); + + if (data->clk) { + mutex_unlock(&data->lock); + return data->clk; + } + + gpio = of_get_named_gpio_flags(data->node, "enable-gpios", 0, NULL); + if (gpio < 0) { + mutex_unlock(&data->lock); + if (gpio != -EPROBE_DEFER) + pr_err("%s: %s: Can't get 'enable-gpios' DT property\n", + __func__, clk_name); + return ERR_PTR(gpio); + } + gpiod = gpio_to_desc(gpio); + + parent_name = of_clk_get_parent_name(data->node, 0); + + clk = clk_register_gpio_gate(NULL, clk_name, parent_name, gpiod, 0); + if (IS_ERR(clk)) { + mutex_unlock(&data->lock); + return clk; + } + + data->clk = clk; + mutex_unlock(&data->lock); + + return clk; +} + +/** + * of_gpio_gate_clk_setup() - Setup function for gpio controlled clock + */ +void __init of_gpio_gate_clk_setup(struct device_node *node) +{ + struct clk_gpio_gate_delayed_register_data *data; + + data = kzalloc(sizeof(struct clk_gpio_gate_delayed_register_data), + GFP_KERNEL); + if (!data) + return; + + data->node = node; + mutex_init(&data->lock); + + of_clk_add_provider(node, of_clk_gpio_gate_delayed_register_get, data); +} +EXPORT_SYMBOL_GPL(of_gpio_gate_clk_setup); +CLK_OF_DECLARE(gpio_gate_clk, "gpio-gate-clock", of_gpio_gate_clk_setup); +#endif diff --git a/drivers/clk/clk-max-gen.c b/drivers/clk/clk-max-gen.c new file mode 100644 index 0000000000000000000000000000000000000000..6505049d50f1e54720864b58694ef0be26e314b9 --- /dev/null +++ b/drivers/clk/clk-max-gen.c @@ -0,0 +1,192 @@ +/* + * clk-max-gen.c - Generic clock driver for Maxim PMICs clocks + * + * Copyright (C) 2014 Google, Inc + * + * Copyright (C) 2012 Samsung Electornics + * Jonghwa Lee + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This driver is based on clk-max77686.c + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct max_gen_clk { + struct regmap *regmap; + u32 mask; + u32 reg; + struct clk_hw hw; +}; + +static struct max_gen_clk *to_max_gen_clk(struct clk_hw *hw) +{ + return container_of(hw, struct max_gen_clk, hw); +} + +static int max_gen_clk_prepare(struct clk_hw *hw) +{ + struct max_gen_clk *max_gen = to_max_gen_clk(hw); + + return regmap_update_bits(max_gen->regmap, max_gen->reg, + max_gen->mask, max_gen->mask); +} + +static void max_gen_clk_unprepare(struct clk_hw *hw) +{ + struct max_gen_clk *max_gen = to_max_gen_clk(hw); + + regmap_update_bits(max_gen->regmap, max_gen->reg, + max_gen->mask, ~max_gen->mask); +} + +static int max_gen_clk_is_prepared(struct clk_hw *hw) +{ + struct max_gen_clk *max_gen = to_max_gen_clk(hw); + int ret; + u32 val; + + ret = regmap_read(max_gen->regmap, max_gen->reg, &val); + + if (ret < 0) + return -EINVAL; + + return val & max_gen->mask; +} + +static unsigned long max_gen_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 32768; +} + +struct clk_ops max_gen_clk_ops = { + .prepare = max_gen_clk_prepare, + .unprepare = max_gen_clk_unprepare, + .is_prepared = max_gen_clk_is_prepared, + .recalc_rate = max_gen_recalc_rate, +}; +EXPORT_SYMBOL_GPL(max_gen_clk_ops); + +static struct clk *max_gen_clk_register(struct device *dev, + struct max_gen_clk *max_gen) +{ + struct clk *clk; + struct clk_hw *hw = &max_gen->hw; + int ret; + + clk = devm_clk_register(dev, hw); + if (IS_ERR(clk)) + return clk; + + ret = clk_register_clkdev(clk, hw->init->name, NULL); + + if (ret) + return ERR_PTR(ret); + + return clk; +} + +int max_gen_clk_probe(struct platform_device *pdev, struct regmap *regmap, + u32 reg, struct clk_init_data *clks_init, int num_init) +{ + int i, ret; + struct max_gen_clk *max_gen_clks; + struct clk **clocks; + struct device *dev = pdev->dev.parent; + const char *clk_name; + struct clk_init_data *init; + + clocks = devm_kzalloc(dev, sizeof(struct clk *) * num_init, GFP_KERNEL); + if (!clocks) + return -ENOMEM; + + max_gen_clks = devm_kzalloc(dev, sizeof(struct max_gen_clk) + * num_init, GFP_KERNEL); + if (!max_gen_clks) + return -ENOMEM; + + for (i = 0; i < num_init; i++) { + max_gen_clks[i].regmap = regmap; + max_gen_clks[i].mask = 1 << i; + max_gen_clks[i].reg = reg; + + init = devm_kzalloc(dev, sizeof(*init), GFP_KERNEL); + if (!init) + return -ENOMEM; + + if (dev->of_node && + !of_property_read_string_index(dev->of_node, + "clock-output-names", + i, &clk_name)) + init->name = clk_name; + else + init->name = clks_init[i].name; + + init->ops = clks_init[i].ops; + init->flags = clks_init[i].flags; + + max_gen_clks[i].hw.init = init; + + clocks[i] = max_gen_clk_register(dev, &max_gen_clks[i]); + if (IS_ERR(clocks[i])) { + ret = PTR_ERR(clocks[i]); + dev_err(dev, "failed to register %s\n", + max_gen_clks[i].hw.init->name); + return ret; + } + } + + platform_set_drvdata(pdev, clocks); + + if (dev->of_node) { + struct clk_onecell_data *of_data; + + of_data = devm_kzalloc(dev, sizeof(*of_data), GFP_KERNEL); + if (!of_data) + return -ENOMEM; + + of_data->clks = clocks; + of_data->clk_num = num_init; + ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, + of_data); + + if (ret) { + dev_err(dev, "failed to register OF clock provider\n"); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(max_gen_clk_probe); + +int max_gen_clk_remove(struct platform_device *pdev, int num_init) +{ + struct device *dev = pdev->dev.parent; + + if (dev->of_node) + of_clk_del_provider(dev->of_node); + + return 0; +} +EXPORT_SYMBOL_GPL(max_gen_clk_remove); diff --git a/drivers/clk/clk-max-gen.h b/drivers/clk/clk-max-gen.h new file mode 100644 index 0000000000000000000000000000000000000000..997e86fc3f4dc4482a8c0aa24bfaae9606f1f106 --- /dev/null +++ b/drivers/clk/clk-max-gen.h @@ -0,0 +1,32 @@ +/* + * clk-max-gen.h - Generic clock driver for Maxim PMICs clocks + * + * Copyright (C) 2014 Google, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __CLK_MAX_GEN_H__ +#define __CLK_MAX_GEN_H__ + +#include +#include +#include +#include +#include + +int max_gen_clk_probe(struct platform_device *pdev, struct regmap *regmap, + u32 reg, struct clk_init_data *clks_init, int num_init); +int max_gen_clk_remove(struct platform_device *pdev, int num_init); +extern struct clk_ops max_gen_clk_ops; + +#endif /* __CLK_MAX_GEN_H__ */ diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c index 3d7e8dd8fd58ffba2dba7c39451bfa3f0d09ba0a..86cdb3a28629ab09394292e2b210eb5b689b4504 100644 --- a/drivers/clk/clk-max77686.c +++ b/drivers/clk/clk-max77686.c @@ -30,193 +30,38 @@ #include #include -enum { - MAX77686_CLK_AP = 0, - MAX77686_CLK_CP, - MAX77686_CLK_PMIC, - MAX77686_CLKS_NUM, -}; - -struct max77686_clk { - struct max77686_dev *iodev; - u32 mask; - struct clk_hw hw; - struct clk_lookup *lookup; -}; - -static struct max77686_clk *to_max77686_clk(struct clk_hw *hw) -{ - return container_of(hw, struct max77686_clk, hw); -} - -static int max77686_clk_prepare(struct clk_hw *hw) -{ - struct max77686_clk *max77686 = to_max77686_clk(hw); - - return regmap_update_bits(max77686->iodev->regmap, - MAX77686_REG_32KHZ, max77686->mask, - max77686->mask); -} - -static void max77686_clk_unprepare(struct clk_hw *hw) -{ - struct max77686_clk *max77686 = to_max77686_clk(hw); - - regmap_update_bits(max77686->iodev->regmap, - MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask); -} - -static int max77686_clk_is_prepared(struct clk_hw *hw) -{ - struct max77686_clk *max77686 = to_max77686_clk(hw); - int ret; - u32 val; - - ret = regmap_read(max77686->iodev->regmap, - MAX77686_REG_32KHZ, &val); - - if (ret < 0) - return -EINVAL; - - return val & max77686->mask; -} - -static unsigned long max77686_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - return 32768; -} - -static struct clk_ops max77686_clk_ops = { - .prepare = max77686_clk_prepare, - .unprepare = max77686_clk_unprepare, - .is_prepared = max77686_clk_is_prepared, - .recalc_rate = max77686_recalc_rate, -}; +#include +#include "clk-max-gen.h" static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = { [MAX77686_CLK_AP] = { .name = "32khz_ap", - .ops = &max77686_clk_ops, + .ops = &max_gen_clk_ops, .flags = CLK_IS_ROOT, }, [MAX77686_CLK_CP] = { .name = "32khz_cp", - .ops = &max77686_clk_ops, + .ops = &max_gen_clk_ops, .flags = CLK_IS_ROOT, }, [MAX77686_CLK_PMIC] = { .name = "32khz_pmic", - .ops = &max77686_clk_ops, + .ops = &max_gen_clk_ops, .flags = CLK_IS_ROOT, }, }; -static struct clk *max77686_clk_register(struct device *dev, - struct max77686_clk *max77686) -{ - struct clk *clk; - struct clk_hw *hw = &max77686->hw; - - clk = clk_register(dev, hw); - if (IS_ERR(clk)) - return clk; - - max77686->lookup = kzalloc(sizeof(struct clk_lookup), GFP_KERNEL); - if (!max77686->lookup) - return ERR_PTR(-ENOMEM); - - max77686->lookup->con_id = hw->init->name; - max77686->lookup->clk = clk; - - clkdev_add(max77686->lookup); - - return clk; -} - static int max77686_clk_probe(struct platform_device *pdev) { struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct max77686_clk *max77686_clks[MAX77686_CLKS_NUM]; - struct clk **clocks; - int i, ret; - - clocks = devm_kzalloc(&pdev->dev, sizeof(struct clk *) - * MAX77686_CLKS_NUM, GFP_KERNEL); - if (!clocks) - return -ENOMEM; - - for (i = 0; i < MAX77686_CLKS_NUM; i++) { - max77686_clks[i] = devm_kzalloc(&pdev->dev, - sizeof(struct max77686_clk), GFP_KERNEL); - if (!max77686_clks[i]) - return -ENOMEM; - } - - for (i = 0; i < MAX77686_CLKS_NUM; i++) { - max77686_clks[i]->iodev = iodev; - max77686_clks[i]->mask = 1 << i; - max77686_clks[i]->hw.init = &max77686_clks_init[i]; - - clocks[i] = max77686_clk_register(&pdev->dev, max77686_clks[i]); - if (IS_ERR(clocks[i])) { - ret = PTR_ERR(clocks[i]); - dev_err(&pdev->dev, "failed to register %s\n", - max77686_clks[i]->hw.init->name); - goto err_clocks; - } - } - - platform_set_drvdata(pdev, clocks); - - if (iodev->dev->of_node) { - struct clk_onecell_data *of_data; - of_data = devm_kzalloc(&pdev->dev, - sizeof(*of_data), GFP_KERNEL); - if (!of_data) { - ret = -ENOMEM; - goto err_clocks; - } - - of_data->clks = clocks; - of_data->clk_num = MAX77686_CLKS_NUM; - ret = of_clk_add_provider(iodev->dev->of_node, - of_clk_src_onecell_get, of_data); - if (ret) { - dev_err(&pdev->dev, "failed to register OF clock provider\n"); - goto err_clocks; - } - } - - return 0; - -err_clocks: - for (--i; i >= 0; --i) { - clkdev_drop(max77686_clks[i]->lookup); - clk_unregister(max77686_clks[i]->hw.clk); - } - - return ret; + return max_gen_clk_probe(pdev, iodev->regmap, MAX77686_REG_32KHZ, + max77686_clks_init, MAX77686_CLKS_NUM); } static int max77686_clk_remove(struct platform_device *pdev) { - struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct clk **clocks = platform_get_drvdata(pdev); - int i; - - if (iodev->dev->of_node) - of_clk_del_provider(iodev->dev->of_node); - - for (i = 0; i < MAX77686_CLKS_NUM; i++) { - struct clk_hw *hw = __clk_get_hw(clocks[i]); - struct max77686_clk *max77686 = to_max77686_clk(hw); - - clkdev_drop(max77686->lookup); - clk_unregister(clocks[i]); - } - return 0; + return max_gen_clk_remove(pdev, MAX77686_CLKS_NUM); } static const struct platform_device_id max77686_clk_id[] = { @@ -228,24 +73,13 @@ MODULE_DEVICE_TABLE(platform, max77686_clk_id); static struct platform_driver max77686_clk_driver = { .driver = { .name = "max77686-clk", - .owner = THIS_MODULE, }, .probe = max77686_clk_probe, .remove = max77686_clk_remove, .id_table = max77686_clk_id, }; -static int __init max77686_clk_init(void) -{ - return platform_driver_register(&max77686_clk_driver); -} -subsys_initcall(max77686_clk_init); - -static void __init max77686_clk_cleanup(void) -{ - platform_driver_unregister(&max77686_clk_driver); -} -module_exit(max77686_clk_cleanup); +module_platform_driver(max77686_clk_driver); MODULE_DESCRIPTION("MAXIM 77686 Clock Driver"); MODULE_AUTHOR("Jonghwa Lee "); diff --git a/drivers/clk/clk-max77802.c b/drivers/clk/clk-max77802.c new file mode 100644 index 0000000000000000000000000000000000000000..0729dc723a8ff81099bc9e0071a9784f6fd9e4d3 --- /dev/null +++ b/drivers/clk/clk-max77802.c @@ -0,0 +1,97 @@ +/* + * clk-max77802.c - Clock driver for Maxim 77802 + * + * Copyright (C) 2014 Google, Inc + * + * Copyright (C) 2012 Samsung Electornics + * Jonghwa Lee + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This driver is based on clk-max77686.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "clk-max-gen.h" + +#define MAX77802_CLOCK_OPMODE_MASK 0x1 +#define MAX77802_CLOCK_LOW_JITTER_SHIFT 0x3 + +static struct clk_init_data max77802_clks_init[MAX77802_CLKS_NUM] = { + [MAX77802_CLK_32K_AP] = { + .name = "32khz_ap", + .ops = &max_gen_clk_ops, + .flags = CLK_IS_ROOT, + }, + [MAX77802_CLK_32K_CP] = { + .name = "32khz_cp", + .ops = &max_gen_clk_ops, + .flags = CLK_IS_ROOT, + }, +}; + +static int max77802_clk_probe(struct platform_device *pdev) +{ + struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); + int ret; + + ret = max_gen_clk_probe(pdev, iodev->regmap, MAX77802_REG_32KHZ, + max77802_clks_init, MAX77802_CLKS_NUM); + + if (ret) { + dev_err(&pdev->dev, "generic probe failed %d\n", ret); + return ret; + } + + /* Enable low-jitter mode on the 32khz clocks. */ + ret = regmap_update_bits(iodev->regmap, MAX77802_REG_32KHZ, + 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT, + 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT); + if (ret < 0) + dev_err(&pdev->dev, "failed to enable low-jitter mode\n"); + + return ret; +} + +static int max77802_clk_remove(struct platform_device *pdev) +{ + return max_gen_clk_remove(pdev, MAX77802_CLKS_NUM); +} + +static const struct platform_device_id max77802_clk_id[] = { + { "max77802-clk", 0}, + { }, +}; +MODULE_DEVICE_TABLE(platform, max77802_clk_id); + +static struct platform_driver max77802_clk_driver = { + .driver = { + .name = "max77802-clk", + }, + .probe = max77802_clk_probe, + .remove = max77802_clk_remove, + .id_table = max77802_clk_id, +}; + +module_platform_driver(max77802_clk_driver); + +MODULE_DESCRIPTION("MAXIM 77802 Clock Driver"); +MODULE_AUTHOR("Javier Martinez Canillas "); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/clk-palmas.c b/drivers/clk/clk-palmas.c index 781630e1372be6151695d7347c4bf439ea58bda7..8d459923a15fd591c782752f79393bb20f4b4da9 100644 --- a/drivers/clk/clk-palmas.c +++ b/drivers/clk/clk-palmas.c @@ -292,7 +292,6 @@ static int palmas_clks_remove(struct platform_device *pdev) static struct platform_driver palmas_clks_driver = { .driver = { .name = "palmas-clk", - .owner = THIS_MODULE, .of_match_table = palmas_clks_of_match, }, .probe = palmas_clks_probe, diff --git a/drivers/clk/clk-rk808.c b/drivers/clk/clk-rk808.c new file mode 100644 index 0000000000000000000000000000000000000000..83902b9cd49ef0a5772e807806999a1d16786195 --- /dev/null +++ b/drivers/clk/clk-rk808.c @@ -0,0 +1,170 @@ +/* + * Clkout driver for Rockchip RK808 + * + * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd + * + * Author:Chris Zhong + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define RK808_NR_OUTPUT 2 + +struct rk808_clkout { + struct rk808 *rk808; + struct clk_onecell_data clk_data; + struct clk_hw clkout1_hw; + struct clk_hw clkout2_hw; +}; + +static unsigned long rk808_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 32768; +} + +static int rk808_clkout2_enable(struct clk_hw *hw, bool enable) +{ + struct rk808_clkout *rk808_clkout = container_of(hw, + struct rk808_clkout, + clkout2_hw); + struct rk808 *rk808 = rk808_clkout->rk808; + + return regmap_update_bits(rk808->regmap, RK808_CLK32OUT_REG, + CLK32KOUT2_EN, enable ? CLK32KOUT2_EN : 0); +} + +static int rk808_clkout2_prepare(struct clk_hw *hw) +{ + return rk808_clkout2_enable(hw, true); +} + +static void rk808_clkout2_unprepare(struct clk_hw *hw) +{ + rk808_clkout2_enable(hw, false); +} + +static int rk808_clkout2_is_prepared(struct clk_hw *hw) +{ + struct rk808_clkout *rk808_clkout = container_of(hw, + struct rk808_clkout, + clkout2_hw); + struct rk808 *rk808 = rk808_clkout->rk808; + uint32_t val; + + int ret = regmap_read(rk808->regmap, RK808_CLK32OUT_REG, &val); + + if (ret < 0) + return ret; + + return (val & CLK32KOUT2_EN) ? 1 : 0; +} + +static const struct clk_ops rk808_clkout1_ops = { + .recalc_rate = rk808_clkout_recalc_rate, +}; + +static const struct clk_ops rk808_clkout2_ops = { + .prepare = rk808_clkout2_prepare, + .unprepare = rk808_clkout2_unprepare, + .is_prepared = rk808_clkout2_is_prepared, + .recalc_rate = rk808_clkout_recalc_rate, +}; + +static int rk808_clkout_probe(struct platform_device *pdev) +{ + struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent); + struct i2c_client *client = rk808->i2c; + struct device_node *node = client->dev.of_node; + struct clk_init_data init = {}; + struct clk **clk_table; + struct rk808_clkout *rk808_clkout; + + rk808_clkout = devm_kzalloc(&client->dev, + sizeof(*rk808_clkout), GFP_KERNEL); + if (!rk808_clkout) + return -ENOMEM; + + rk808_clkout->rk808 = rk808; + + clk_table = devm_kcalloc(&client->dev, RK808_NR_OUTPUT, + sizeof(struct clk *), GFP_KERNEL); + if (!clk_table) + return -ENOMEM; + + init.flags = CLK_IS_ROOT; + init.parent_names = NULL; + init.num_parents = 0; + init.name = "rk808-clkout1"; + init.ops = &rk808_clkout1_ops; + rk808_clkout->clkout1_hw.init = &init; + + /* optional override of the clockname */ + of_property_read_string_index(node, "clock-output-names", + 0, &init.name); + + clk_table[0] = devm_clk_register(&client->dev, + &rk808_clkout->clkout1_hw); + if (IS_ERR(clk_table[0])) + return PTR_ERR(clk_table[0]); + + init.name = "rk808-clkout2"; + init.ops = &rk808_clkout2_ops; + rk808_clkout->clkout2_hw.init = &init; + + /* optional override of the clockname */ + of_property_read_string_index(node, "clock-output-names", + 1, &init.name); + + clk_table[1] = devm_clk_register(&client->dev, + &rk808_clkout->clkout2_hw); + if (IS_ERR(clk_table[1])) + return PTR_ERR(clk_table[1]); + + rk808_clkout->clk_data.clks = clk_table; + rk808_clkout->clk_data.clk_num = RK808_NR_OUTPUT; + + return of_clk_add_provider(node, of_clk_src_onecell_get, + &rk808_clkout->clk_data); +} + +static int rk808_clkout_remove(struct platform_device *pdev) +{ + struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent); + struct i2c_client *client = rk808->i2c; + struct device_node *node = client->dev.of_node; + + of_clk_del_provider(node); + + return 0; +} + +static struct platform_driver rk808_clkout_driver = { + .probe = rk808_clkout_probe, + .remove = rk808_clkout_remove, + .driver = { + .name = "rk808-clkout", + }, +}; + +module_platform_driver(rk808_clkout_driver); + +MODULE_DESCRIPTION("Clkout driver for the rk808 series PMICs"); +MODULE_AUTHOR("Chris Zhong "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rk808-clkout"); diff --git a/drivers/clk/clk-twl6040.c b/drivers/clk/clk-twl6040.c index 1ada79a2805297aa4b3a6d921b93159bd951a402..4a755135bcd388433696ee4c651a5bbb03302f5b 100644 --- a/drivers/clk/clk-twl6040.c +++ b/drivers/clk/clk-twl6040.c @@ -112,7 +112,6 @@ static int twl6040_clk_remove(struct platform_device *pdev) static struct platform_driver twl6040_clk_driver = { .driver = { .name = "twl6040-clk", - .owner = THIS_MODULE, }, .probe = twl6040_clk_probe, .remove = twl6040_clk_remove, diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index b131041c8f488f29f4a9f9d5161da119f274245e..ef67719f4e52c23cd977844662e3a202be4963bc 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -395,7 +395,6 @@ static struct platform_driver wm831x_clk_driver = { .probe = wm831x_clk_probe, .driver = { .name = "wm831x-clk", - .owner = THIS_MODULE, }, }; diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index bacc06ff939b090bffb9ef9759ac9b9058520a98..4896ae9e23da0c23ed52872cf1992f95609e1b59 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -100,6 +100,8 @@ static void clk_enable_unlock(unsigned long flags) static struct dentry *rootdir; static int inited = 0; +static DEFINE_MUTEX(clk_debug_lock); +static HLIST_HEAD(clk_debug_list); static struct hlist_head *all_lists[] = { &clk_root_list, @@ -117,11 +119,11 @@ static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level) if (!c) return; - seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu\n", + seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu %-3d\n", level * 3 + 1, "", 30 - level * 3, c->name, c->enable_count, c->prepare_count, clk_get_rate(c), - clk_get_accuracy(c)); + clk_get_accuracy(c), clk_get_phase(c)); } static void clk_summary_show_subtree(struct seq_file *s, struct clk *c, @@ -143,8 +145,8 @@ static int clk_summary_show(struct seq_file *s, void *data) struct clk *c; struct hlist_head **lists = (struct hlist_head **)s->private; - seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy\n"); - seq_puts(s, "--------------------------------------------------------------------------------\n"); + seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy phase\n"); + seq_puts(s, "----------------------------------------------------------------------------------------\n"); clk_prepare_lock(); @@ -180,6 +182,7 @@ static void clk_dump_one(struct seq_file *s, struct clk *c, int level) seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); seq_printf(s, "\"rate\": %lu", clk_get_rate(c)); seq_printf(s, "\"accuracy\": %lu", clk_get_accuracy(c)); + seq_printf(s, "\"phase\": %d", clk_get_phase(c)); } static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level) @@ -264,6 +267,11 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry) if (!d) goto err_out; + d = debugfs_create_u32("clk_phase", S_IRUGO, clk->dentry, + (u32 *)&clk->phase); + if (!d) + goto err_out; + d = debugfs_create_x32("clk_flags", S_IRUGO, clk->dentry, (u32 *)&clk->flags); if (!d) @@ -300,28 +308,6 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry) return ret; } -/* caller must hold prepare_lock */ -static int clk_debug_create_subtree(struct clk *clk, struct dentry *pdentry) -{ - struct clk *child; - int ret = -EINVAL;; - - if (!clk || !pdentry) - goto out; - - ret = clk_debug_create_one(clk, pdentry); - - if (ret) - goto out; - - hlist_for_each_entry(child, &clk->children, child_node) - clk_debug_create_subtree(child, pdentry); - - ret = 0; -out: - return ret; -} - /** * clk_debug_register - add a clk node to the debugfs clk tree * @clk: the clk being added to the debugfs clk tree @@ -329,20 +315,21 @@ static int clk_debug_create_subtree(struct clk *clk, struct dentry *pdentry) * Dynamically adds a clk to the debugfs clk tree if debugfs has been * initialized. Otherwise it bails out early since the debugfs clk tree * will be created lazily by clk_debug_init as part of a late_initcall. - * - * Caller must hold prepare_lock. Only clk_init calls this function (so - * far) so this is taken care. */ static int clk_debug_register(struct clk *clk) { int ret = 0; + mutex_lock(&clk_debug_lock); + hlist_add_head(&clk->debug_node, &clk_debug_list); + if (!inited) - goto out; + goto unlock; - ret = clk_debug_create_subtree(clk, rootdir); + ret = clk_debug_create_one(clk, rootdir); +unlock: + mutex_unlock(&clk_debug_lock); -out: return ret; } @@ -353,12 +340,18 @@ static int clk_debug_register(struct clk *clk) * Dynamically removes a clk and all it's children clk nodes from the * debugfs clk tree if clk->dentry points to debugfs created by * clk_debug_register in __clk_init. - * - * Caller must hold prepare_lock. */ static void clk_debug_unregister(struct clk *clk) { + mutex_lock(&clk_debug_lock); + if (!clk->dentry) + goto out; + + hlist_del_init(&clk->debug_node); debugfs_remove_recursive(clk->dentry); + clk->dentry = NULL; +out: + mutex_unlock(&clk_debug_lock); } struct dentry *clk_debugfs_add_file(struct clk *clk, char *name, umode_t mode, @@ -415,17 +408,12 @@ static int __init clk_debug_init(void) if (!d) return -ENOMEM; - clk_prepare_lock(); - - hlist_for_each_entry(clk, &clk_root_list, child_node) - clk_debug_create_subtree(clk, rootdir); - - hlist_for_each_entry(clk, &clk_orphan_list, child_node) - clk_debug_create_subtree(clk, rootdir); + mutex_lock(&clk_debug_lock); + hlist_for_each_entry(clk, &clk_debug_list, debug_node) + clk_debug_create_one(clk, rootdir); inited = 1; - - clk_prepare_unlock(); + mutex_unlock(&clk_debug_lock); return 0; } @@ -1743,6 +1731,77 @@ int clk_set_parent(struct clk *clk, struct clk *parent) } EXPORT_SYMBOL_GPL(clk_set_parent); +/** + * clk_set_phase - adjust the phase shift of a clock signal + * @clk: clock signal source + * @degrees: number of degrees the signal is shifted + * + * Shifts the phase of a clock signal by the specified + * degrees. Returns 0 on success, -EERROR otherwise. + * + * This function makes no distinction about the input or reference + * signal that we adjust the clock signal phase against. For example + * phase locked-loop clock signal generators we may shift phase with + * respect to feedback clock signal input, but for other cases the + * clock phase may be shifted with respect to some other, unspecified + * signal. + * + * Additionally the concept of phase shift does not propagate through + * the clock tree hierarchy, which sets it apart from clock rates and + * clock accuracy. A parent clock phase attribute does not have an + * impact on the phase attribute of a child clock. + */ +int clk_set_phase(struct clk *clk, int degrees) +{ + int ret = 0; + + if (!clk) + goto out; + + /* sanity check degrees */ + degrees %= 360; + if (degrees < 0) + degrees += 360; + + clk_prepare_lock(); + + if (!clk->ops->set_phase) + goto out_unlock; + + ret = clk->ops->set_phase(clk->hw, degrees); + + if (!ret) + clk->phase = degrees; + +out_unlock: + clk_prepare_unlock(); + +out: + return ret; +} + +/** + * clk_get_phase - return the phase shift of a clock signal + * @clk: clock signal source + * + * Returns the phase shift of a clock node in degrees, otherwise returns + * -EERROR. + */ +int clk_get_phase(struct clk *clk) +{ + int ret = 0; + + if (!clk) + goto out; + + clk_prepare_lock(); + ret = clk->phase; + clk_prepare_unlock(); + +out: + return ret; +} + /** * __clk_init - initialize the data structures in a struct clk * @dev: device initializing this clk, placeholder for now @@ -1861,6 +1920,16 @@ int __clk_init(struct device *dev, struct clk *clk) else clk->accuracy = 0; + /* + * Set clk's phase. + * Since a phase is by definition relative to its parent, just + * query the current clock phase, or just assume it's in phase. + */ + if (clk->ops->get_phase) + clk->phase = clk->ops->get_phase(clk->hw); + else + clk->phase = 0; + /* * Set clk's rate. The preferred method is to use .recalc_rate. For * simple clocks and lazy developers the default fallback is to use the @@ -2092,14 +2161,16 @@ void clk_unregister(struct clk *clk) { unsigned long flags; - if (!clk || WARN_ON_ONCE(IS_ERR(clk))) - return; + if (!clk || WARN_ON_ONCE(IS_ERR(clk))) + return; + + clk_debug_unregister(clk); clk_prepare_lock(); if (clk->ops == &clk_nodrv_ops) { pr_err("%s: unregistered clock: %s\n", __func__, clk->name); - goto out; + return; } /* * Assign empty clock ops for consumers that might still hold @@ -2118,16 +2189,13 @@ void clk_unregister(struct clk *clk) clk_set_parent(child, NULL); } - clk_debug_unregister(clk); - hlist_del_init(&clk->child_node); if (clk->prepare_count) pr_warn("%s: unregistering prepared clock: %s\n", __func__, clk->name); - kref_put(&clk->ref, __clk_release); -out: + clk_prepare_unlock(); } EXPORT_SYMBOL_GPL(clk_unregister); diff --git a/drivers/clk/hisilicon/clk-hix5hd2.c b/drivers/clk/hisilicon/clk-hix5hd2.c index e5fcfb4e32efa2b8bb24d32bb7ac19fec74b2c55..3f369c60fe56a9f6d449ef82c51ffca141cf8aee 100644 --- a/drivers/clk/hisilicon/clk-hix5hd2.c +++ b/drivers/clk/hisilicon/clk-hix5hd2.c @@ -9,6 +9,8 @@ #include #include +#include +#include #include "clk.h" static struct hisi_fixed_rate_clock hix5hd2_fixed_rate_clks[] __initdata = { @@ -48,9 +50,9 @@ static const char *sfc_mux_p[] __initconst = { "24m", "150m", "200m", "100m", "75m", }; static u32 sfc_mux_table[] = {0, 4, 5, 6, 7}; -static const char *sdio1_mux_p[] __initconst = { +static const char *sdio_mux_p[] __initconst = { "75m", "100m", "50m", "15m", }; -static u32 sdio1_mux_table[] = {0, 1, 2, 3}; +static u32 sdio_mux_table[] = {0, 1, 2, 3}; static const char *fephy_mux_p[] __initconst = { "25m", "125m"}; static u32 fephy_mux_table[] = {0, 1}; @@ -59,28 +61,243 @@ static u32 fephy_mux_table[] = {0, 1}; static struct hisi_mux_clock hix5hd2_mux_clks[] __initdata = { { HIX5HD2_SFC_MUX, "sfc_mux", sfc_mux_p, ARRAY_SIZE(sfc_mux_p), CLK_SET_RATE_PARENT, 0x5c, 8, 3, 0, sfc_mux_table, }, - { HIX5HD2_MMC_MUX, "mmc_mux", sdio1_mux_p, ARRAY_SIZE(sdio1_mux_p), - CLK_SET_RATE_PARENT, 0xa0, 8, 2, 0, sdio1_mux_table, }, + { HIX5HD2_MMC_MUX, "mmc_mux", sdio_mux_p, ARRAY_SIZE(sdio_mux_p), + CLK_SET_RATE_PARENT, 0xa0, 8, 2, 0, sdio_mux_table, }, + { HIX5HD2_SD_MUX, "sd_mux", sdio_mux_p, ARRAY_SIZE(sdio_mux_p), + CLK_SET_RATE_PARENT, 0x9c, 8, 2, 0, sdio_mux_table, }, { HIX5HD2_FEPHY_MUX, "fephy_mux", fephy_mux_p, ARRAY_SIZE(fephy_mux_p), CLK_SET_RATE_PARENT, 0x120, 8, 2, 0, fephy_mux_table, }, }; static struct hisi_gate_clock hix5hd2_gate_clks[] __initdata = { - /*sfc*/ + /* sfc */ { HIX5HD2_SFC_CLK, "clk_sfc", "sfc_mux", CLK_SET_RATE_PARENT, 0x5c, 0, 0, }, { HIX5HD2_SFC_RST, "rst_sfc", "clk_sfc", CLK_SET_RATE_PARENT, 0x5c, 4, CLK_GATE_SET_TO_DISABLE, }, - /*sdio1*/ + /* sdio0 */ + { HIX5HD2_SD_BIU_CLK, "clk_sd_biu", "200m", + CLK_SET_RATE_PARENT, 0x9c, 0, 0, }, + { HIX5HD2_SD_CIU_CLK, "clk_sd_ciu", "sd_mux", + CLK_SET_RATE_PARENT, 0x9c, 1, 0, }, + { HIX5HD2_SD_CIU_RST, "rst_sd_ciu", "clk_sd_ciu", + CLK_SET_RATE_PARENT, 0x9c, 4, CLK_GATE_SET_TO_DISABLE, }, + /* sdio1 */ { HIX5HD2_MMC_BIU_CLK, "clk_mmc_biu", "200m", CLK_SET_RATE_PARENT, 0xa0, 0, 0, }, { HIX5HD2_MMC_CIU_CLK, "clk_mmc_ciu", "mmc_mux", CLK_SET_RATE_PARENT, 0xa0, 1, 0, }, { HIX5HD2_MMC_CIU_RST, "rst_mmc_ciu", "clk_mmc_ciu", CLK_SET_RATE_PARENT, 0xa0, 4, CLK_GATE_SET_TO_DISABLE, }, + /* gsf */ + { HIX5HD2_FWD_BUS_CLK, "clk_fwd_bus", NULL, 0, 0xcc, 0, 0, }, + { HIX5HD2_FWD_SYS_CLK, "clk_fwd_sys", "clk_fwd_bus", 0, 0xcc, 5, 0, }, + { HIX5HD2_MAC0_PHY_CLK, "clk_fephy", "clk_fwd_sys", + CLK_SET_RATE_PARENT, 0x120, 0, 0, }, + /* wdg0 */ + { HIX5HD2_WDG0_CLK, "clk_wdg0", "24m", + CLK_SET_RATE_PARENT, 0x178, 0, 0, }, + { HIX5HD2_WDG0_RST, "rst_wdg0", "clk_wdg0", + CLK_SET_RATE_PARENT, 0x178, 4, CLK_GATE_SET_TO_DISABLE, }, + /* I2C */ + {HIX5HD2_I2C0_CLK, "clk_i2c0", "100m", + CLK_SET_RATE_PARENT, 0x06c, 4, 0, }, + {HIX5HD2_I2C0_RST, "rst_i2c0", "clk_i2c0", + CLK_SET_RATE_PARENT, 0x06c, 5, CLK_GATE_SET_TO_DISABLE, }, + {HIX5HD2_I2C1_CLK, "clk_i2c1", "100m", + CLK_SET_RATE_PARENT, 0x06c, 8, 0, }, + {HIX5HD2_I2C1_RST, "rst_i2c1", "clk_i2c1", + CLK_SET_RATE_PARENT, 0x06c, 9, CLK_GATE_SET_TO_DISABLE, }, + {HIX5HD2_I2C2_CLK, "clk_i2c2", "100m", + CLK_SET_RATE_PARENT, 0x06c, 12, 0, }, + {HIX5HD2_I2C2_RST, "rst_i2c2", "clk_i2c2", + CLK_SET_RATE_PARENT, 0x06c, 13, CLK_GATE_SET_TO_DISABLE, }, + {HIX5HD2_I2C3_CLK, "clk_i2c3", "100m", + CLK_SET_RATE_PARENT, 0x06c, 16, 0, }, + {HIX5HD2_I2C3_RST, "rst_i2c3", "clk_i2c3", + CLK_SET_RATE_PARENT, 0x06c, 17, CLK_GATE_SET_TO_DISABLE, }, + {HIX5HD2_I2C4_CLK, "clk_i2c4", "100m", + CLK_SET_RATE_PARENT, 0x06c, 20, 0, }, + {HIX5HD2_I2C4_RST, "rst_i2c4", "clk_i2c4", + CLK_SET_RATE_PARENT, 0x06c, 21, CLK_GATE_SET_TO_DISABLE, }, + {HIX5HD2_I2C5_CLK, "clk_i2c5", "100m", + CLK_SET_RATE_PARENT, 0x06c, 0, 0, }, + {HIX5HD2_I2C5_RST, "rst_i2c5", "clk_i2c5", + CLK_SET_RATE_PARENT, 0x06c, 1, CLK_GATE_SET_TO_DISABLE, }, }; +enum hix5hd2_clk_type { + TYPE_COMPLEX, + TYPE_ETHER, +}; + +struct hix5hd2_complex_clock { + const char *name; + const char *parent_name; + u32 id; + u32 ctrl_reg; + u32 ctrl_clk_mask; + u32 ctrl_rst_mask; + u32 phy_reg; + u32 phy_clk_mask; + u32 phy_rst_mask; + enum hix5hd2_clk_type type; +}; + +struct hix5hd2_clk_complex { + struct clk_hw hw; + u32 id; + void __iomem *ctrl_reg; + u32 ctrl_clk_mask; + u32 ctrl_rst_mask; + void __iomem *phy_reg; + u32 phy_clk_mask; + u32 phy_rst_mask; +}; + +static struct hix5hd2_complex_clock hix5hd2_complex_clks[] __initdata = { + {"clk_mac0", "clk_fephy", HIX5HD2_MAC0_CLK, + 0xcc, 0xa, 0x500, 0x120, 0, 0x10, TYPE_ETHER}, + {"clk_mac1", "clk_fwd_sys", HIX5HD2_MAC1_CLK, + 0xcc, 0x14, 0xa00, 0x168, 0x2, 0, TYPE_ETHER}, + {"clk_sata", NULL, HIX5HD2_SATA_CLK, + 0xa8, 0x1f, 0x300, 0xac, 0x1, 0x0, TYPE_COMPLEX}, + {"clk_usb", NULL, HIX5HD2_USB_CLK, + 0xb8, 0xff, 0x3f000, 0xbc, 0x7, 0x3f00, TYPE_COMPLEX}, +}; + +#define to_complex_clk(_hw) container_of(_hw, struct hix5hd2_clk_complex, hw) + +static int clk_ether_prepare(struct clk_hw *hw) +{ + struct hix5hd2_clk_complex *clk = to_complex_clk(hw); + u32 val; + + val = readl_relaxed(clk->ctrl_reg); + val |= clk->ctrl_clk_mask | clk->ctrl_rst_mask; + writel_relaxed(val, clk->ctrl_reg); + val &= ~(clk->ctrl_rst_mask); + writel_relaxed(val, clk->ctrl_reg); + + val = readl_relaxed(clk->phy_reg); + val |= clk->phy_clk_mask; + val &= ~(clk->phy_rst_mask); + writel_relaxed(val, clk->phy_reg); + mdelay(10); + + val &= ~(clk->phy_clk_mask); + val |= clk->phy_rst_mask; + writel_relaxed(val, clk->phy_reg); + mdelay(10); + + val |= clk->phy_clk_mask; + val &= ~(clk->phy_rst_mask); + writel_relaxed(val, clk->phy_reg); + mdelay(30); + return 0; +} + +static void clk_ether_unprepare(struct clk_hw *hw) +{ + struct hix5hd2_clk_complex *clk = to_complex_clk(hw); + u32 val; + + val = readl_relaxed(clk->ctrl_reg); + val &= ~(clk->ctrl_clk_mask); + writel_relaxed(val, clk->ctrl_reg); +} + +static struct clk_ops clk_ether_ops = { + .prepare = clk_ether_prepare, + .unprepare = clk_ether_unprepare, +}; + +static int clk_complex_enable(struct clk_hw *hw) +{ + struct hix5hd2_clk_complex *clk = to_complex_clk(hw); + u32 val; + + val = readl_relaxed(clk->ctrl_reg); + val |= clk->ctrl_clk_mask; + val &= ~(clk->ctrl_rst_mask); + writel_relaxed(val, clk->ctrl_reg); + + val = readl_relaxed(clk->phy_reg); + val |= clk->phy_clk_mask; + val &= ~(clk->phy_rst_mask); + writel_relaxed(val, clk->phy_reg); + + return 0; +} + +static void clk_complex_disable(struct clk_hw *hw) +{ + struct hix5hd2_clk_complex *clk = to_complex_clk(hw); + u32 val; + + val = readl_relaxed(clk->ctrl_reg); + val |= clk->ctrl_rst_mask; + val &= ~(clk->ctrl_clk_mask); + writel_relaxed(val, clk->ctrl_reg); + + val = readl_relaxed(clk->phy_reg); + val |= clk->phy_rst_mask; + val &= ~(clk->phy_clk_mask); + writel_relaxed(val, clk->phy_reg); +} + +static struct clk_ops clk_complex_ops = { + .enable = clk_complex_enable, + .disable = clk_complex_disable, +}; + +void __init hix5hd2_clk_register_complex(struct hix5hd2_complex_clock *clks, + int nums, struct hisi_clock_data *data) +{ + void __iomem *base = data->base; + int i; + + for (i = 0; i < nums; i++) { + struct hix5hd2_clk_complex *p_clk; + struct clk *clk; + struct clk_init_data init; + + p_clk = kzalloc(sizeof(*p_clk), GFP_KERNEL); + if (!p_clk) + return; + + init.name = clks[i].name; + if (clks[i].type == TYPE_ETHER) + init.ops = &clk_ether_ops; + else + init.ops = &clk_complex_ops; + + init.flags = CLK_IS_BASIC; + init.parent_names = + (clks[i].parent_name ? &clks[i].parent_name : NULL); + init.num_parents = (clks[i].parent_name ? 1 : 0); + + p_clk->ctrl_reg = base + clks[i].ctrl_reg; + p_clk->ctrl_clk_mask = clks[i].ctrl_clk_mask; + p_clk->ctrl_rst_mask = clks[i].ctrl_rst_mask; + p_clk->phy_reg = base + clks[i].phy_reg; + p_clk->phy_clk_mask = clks[i].phy_clk_mask; + p_clk->phy_rst_mask = clks[i].phy_rst_mask; + p_clk->hw.init = &init; + + clk = clk_register(NULL, &p_clk->hw); + if (IS_ERR(clk)) { + kfree(p_clk); + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + + data->clk_data.clks[clks[i].id] = clk; + } +} + static void __init hix5hd2_clk_init(struct device_node *np) { struct hisi_clock_data *clk_data; @@ -96,6 +313,9 @@ static void __init hix5hd2_clk_init(struct device_node *np) clk_data); hisi_clk_register_gate(hix5hd2_gate_clks, ARRAY_SIZE(hix5hd2_gate_clks), clk_data); + hix5hd2_clk_register_complex(hix5hd2_complex_clks, + ARRAY_SIZE(hix5hd2_complex_clks), + clk_data); } CLK_OF_DECLARE(hix5hd2_clk, "hisilicon,hix5hd2-clock", hix5hd2_clk_init); diff --git a/drivers/clk/mvebu/armada-370.c b/drivers/clk/mvebu/armada-370.c index bef198a83863aaa79e21ca1c4e1401238cae31b7..756f0f39d6a3d9f4d3ffcd19a9eae6166c7ba3e3 100644 --- a/drivers/clk/mvebu/armada-370.c +++ b/drivers/clk/mvebu/armada-370.c @@ -23,6 +23,7 @@ */ #define SARL 0 /* Low part [0:31] */ +#define SARL_A370_SSCG_ENABLE BIT(10) #define SARL_A370_PCLK_FREQ_OPT 11 #define SARL_A370_PCLK_FREQ_OPT_MASK 0xF #define SARL_A370_FAB_FREQ_OPT 15 @@ -133,10 +134,17 @@ static void __init a370_get_clk_ratio( } } +static bool a370_is_sscg_enabled(void __iomem *sar) +{ + return !(readl(sar) & SARL_A370_SSCG_ENABLE); +} + static const struct coreclk_soc_desc a370_coreclks = { .get_tclk_freq = a370_get_tclk_freq, .get_cpu_freq = a370_get_cpu_freq, .get_clk_ratio = a370_get_clk_ratio, + .is_sscg_enabled = a370_is_sscg_enabled, + .fix_sscg_deviation = kirkwood_fix_sscg_deviation, .ratios = a370_coreclk_ratios, .num_ratios = ARRAY_SIZE(a370_coreclk_ratios), }; diff --git a/drivers/clk/mvebu/armada-375.c b/drivers/clk/mvebu/armada-375.c index c991a4d95e108d594f7ab0930a202e7eb7ea7fe3..c7af2242b796866a9367b02c0a74827499901a1f 100644 --- a/drivers/clk/mvebu/armada-375.c +++ b/drivers/clk/mvebu/armada-375.c @@ -27,14 +27,14 @@ * all modified at the same time, and not separately as for the Armada * 370 or the Armada XP SoCs. * - * SAR0[21:17] : CPU frequency DDR frequency L2 frequency + * SAR1[21:17] : CPU frequency DDR frequency L2 frequency * 6 = 400 MHz 400 MHz 200 MHz * 15 = 600 MHz 600 MHz 300 MHz * 21 = 800 MHz 534 MHz 400 MHz * 25 = 1000 MHz 500 MHz 500 MHz * others reserved. * - * SAR0[22] : TCLK frequency + * SAR1[22] : TCLK frequency * 0 = 166 MHz * 1 = 200 MHz */ diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c index 25ceccf939ad2f33cdc71907d91e68b8e69a64be..b7fcb469c87af73e6c6f45dcb1b98e188d99c0f8 100644 --- a/drivers/clk/mvebu/common.c +++ b/drivers/clk/mvebu/common.c @@ -26,8 +26,85 @@ * Core Clocks */ +#define SSCG_CONF_MODE(reg) (((reg) >> 16) & 0x3) +#define SSCG_SPREAD_DOWN 0x0 +#define SSCG_SPREAD_UP 0x1 +#define SSCG_SPREAD_CENTRAL 0x2 +#define SSCG_CONF_LOW(reg) (((reg) >> 8) & 0xFF) +#define SSCG_CONF_HIGH(reg) ((reg) & 0xFF) + static struct clk_onecell_data clk_data; +/* + * This function can be used by the Kirkwood, the Armada 370, the + * Armada XP and the Armada 375 SoC. The name of the function was + * chosen following the dt convention: using the first known SoC + * compatible with it. + */ +u32 kirkwood_fix_sscg_deviation(u32 system_clk) +{ + struct device_node *sscg_np = NULL; + void __iomem *sscg_map; + u32 sscg_reg; + s32 low_bound, high_bound; + u64 freq_swing_half; + + sscg_np = of_find_node_by_name(NULL, "sscg"); + if (sscg_np == NULL) { + pr_err("cannot get SSCG register node\n"); + return system_clk; + } + + sscg_map = of_iomap(sscg_np, 0); + if (sscg_map == NULL) { + pr_err("cannot map SSCG register\n"); + goto out; + } + + sscg_reg = readl(sscg_map); + high_bound = SSCG_CONF_HIGH(sscg_reg); + low_bound = SSCG_CONF_LOW(sscg_reg); + + if ((high_bound - low_bound) <= 0) + goto out; + /* + * From Marvell engineer we got the following formula (when + * this code was written, the datasheet was erroneous) + * Spread percentage = 1/96 * (H - L) / H + * H = SSCG_High_Boundary + * L = SSCG_Low_Boundary + * + * As the deviation is half of spread then it lead to the + * following formula in the code. + * + * To avoid an overflow and not lose any significant digit in + * the same time we have to use a 64 bit integer. + */ + + freq_swing_half = (((u64)high_bound - (u64)low_bound) + * (u64)system_clk); + do_div(freq_swing_half, (2 * 96 * high_bound)); + + switch (SSCG_CONF_MODE(sscg_reg)) { + case SSCG_SPREAD_DOWN: + system_clk -= freq_swing_half; + break; + case SSCG_SPREAD_UP: + system_clk += freq_swing_half; + break; + case SSCG_SPREAD_CENTRAL: + default: + break; + } + + iounmap(sscg_map); + +out: + of_node_put(sscg_np); + + return system_clk; +} + void __init mvebu_coreclk_setup(struct device_node *np, const struct coreclk_soc_desc *desc) { @@ -62,6 +139,11 @@ void __init mvebu_coreclk_setup(struct device_node *np, of_property_read_string_index(np, "clock-output-names", 1, &cpuclk_name); rate = desc->get_cpu_freq(base); + + if (desc->is_sscg_enabled && desc->fix_sscg_deviation + && desc->is_sscg_enabled(base)) + rate = desc->fix_sscg_deviation(rate); + clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL, CLK_IS_ROOT, rate); WARN_ON(IS_ERR(clk_data.clks[1])); @@ -89,8 +171,10 @@ void __init mvebu_coreclk_setup(struct device_node *np, * Clock Gating Control */ +DEFINE_SPINLOCK(ctrl_gating_lock); + struct clk_gating_ctrl { - spinlock_t lock; + spinlock_t *lock; struct clk **gates; int num_gates; }; @@ -138,7 +222,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np, if (WARN_ON(!ctrl)) goto ctrl_out; - spin_lock_init(&ctrl->lock); + /* lock must already be initialized */ + ctrl->lock = &ctrl_gating_lock; /* Count, allocate, and register clock gates */ for (n = 0; desc[n].name;) @@ -155,7 +240,7 @@ void __init mvebu_clk_gating_setup(struct device_node *np, (desc[n].parent) ? desc[n].parent : default_parent; ctrl->gates[n] = clk_register_gate(NULL, desc[n].name, parent, desc[n].flags, base, desc[n].bit_idx, - 0, &ctrl->lock); + 0, ctrl->lock); WARN_ON(IS_ERR(ctrl->gates[n])); } diff --git a/drivers/clk/mvebu/common.h b/drivers/clk/mvebu/common.h index f968b4d9df922cff17e9f404ca211c3aa56e53f1..783b5631a453fe614e070a51e760e1004d2580e1 100644 --- a/drivers/clk/mvebu/common.h +++ b/drivers/clk/mvebu/common.h @@ -17,6 +17,8 @@ #include +extern spinlock_t ctrl_gating_lock; + struct device_node; struct coreclk_ratio { @@ -28,6 +30,8 @@ struct coreclk_soc_desc { u32 (*get_tclk_freq)(void __iomem *sar); u32 (*get_cpu_freq)(void __iomem *sar); void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div); + bool (*is_sscg_enabled)(void __iomem *sar); + u32 (*fix_sscg_deviation)(u32 system_clk); const struct coreclk_ratio *ratios; int num_ratios; }; @@ -45,4 +49,9 @@ void __init mvebu_coreclk_setup(struct device_node *np, void __init mvebu_clk_gating_setup(struct device_node *np, const struct clk_gating_soc_desc *desc); +/* + * This function is shared among the Kirkwood, Armada 370, Armada XP + * and Armada 375 SoC + */ +u32 kirkwood_fix_sscg_deviation(u32 system_clk); #endif diff --git a/drivers/clk/mvebu/kirkwood.c b/drivers/clk/mvebu/kirkwood.c index ddb666a86500964201db0ad6d3a3724511302b59..99550f25975ea74b8963bb77910b5cf1614f3594 100644 --- a/drivers/clk/mvebu/kirkwood.c +++ b/drivers/clk/mvebu/kirkwood.c @@ -13,9 +13,11 @@ */ #include +#include #include #include #include +#include #include "common.h" /* @@ -214,7 +216,6 @@ static const struct clk_gating_soc_desc kirkwood_gating_desc[] __initconst = { { "runit", NULL, 7, 0 }, { "xor0", NULL, 8, 0 }, { "audio", NULL, 9, 0 }, - { "powersave", "cpuclk", 11, 0 }, { "sata0", NULL, 14, 0 }, { "sata1", NULL, 15, 0 }, { "xor1", NULL, 16, 0 }, @@ -225,6 +226,101 @@ static const struct clk_gating_soc_desc kirkwood_gating_desc[] __initconst = { { } }; + +/* + * Clock Muxing Control + */ + +struct clk_muxing_soc_desc { + const char *name; + const char **parents; + int num_parents; + int shift; + int width; + unsigned long flags; +}; + +struct clk_muxing_ctrl { + spinlock_t *lock; + struct clk **muxes; + int num_muxes; +}; + +static const char *powersave_parents[] = { + "cpuclk", + "ddrclk", +}; + +static const struct clk_muxing_soc_desc kirkwood_mux_desc[] __initconst = { + { "powersave", powersave_parents, ARRAY_SIZE(powersave_parents), + 11, 1, 0 }, +}; + +#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw) + +static struct clk *clk_muxing_get_src( + struct of_phandle_args *clkspec, void *data) +{ + struct clk_muxing_ctrl *ctrl = (struct clk_muxing_ctrl *)data; + int n; + + if (clkspec->args_count < 1) + return ERR_PTR(-EINVAL); + + for (n = 0; n < ctrl->num_muxes; n++) { + struct clk_mux *mux = + to_clk_mux(__clk_get_hw(ctrl->muxes[n])); + if (clkspec->args[0] == mux->shift) + return ctrl->muxes[n]; + } + return ERR_PTR(-ENODEV); +} + +static void __init kirkwood_clk_muxing_setup(struct device_node *np, + const struct clk_muxing_soc_desc *desc) +{ + struct clk_muxing_ctrl *ctrl; + void __iomem *base; + int n; + + base = of_iomap(np, 0); + if (WARN_ON(!base)) + return; + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (WARN_ON(!ctrl)) + goto ctrl_out; + + /* lock must already be initialized */ + ctrl->lock = &ctrl_gating_lock; + + /* Count, allocate, and register clock muxes */ + for (n = 0; desc[n].name;) + n++; + + ctrl->num_muxes = n; + ctrl->muxes = kcalloc(ctrl->num_muxes, sizeof(struct clk *), + GFP_KERNEL); + if (WARN_ON(!ctrl->muxes)) + goto muxes_out; + + for (n = 0; n < ctrl->num_muxes; n++) { + ctrl->muxes[n] = clk_register_mux(NULL, desc[n].name, + desc[n].parents, desc[n].num_parents, + desc[n].flags, base, desc[n].shift, + desc[n].width, desc[n].flags, ctrl->lock); + WARN_ON(IS_ERR(ctrl->muxes[n])); + } + + of_clk_add_provider(np, clk_muxing_get_src, ctrl); + + return; +muxes_out: + kfree(ctrl); +ctrl_out: + iounmap(base); +} + static void __init kirkwood_clk_init(struct device_node *np) { struct device_node *cgnp = @@ -236,8 +332,10 @@ static void __init kirkwood_clk_init(struct device_node *np) else mvebu_coreclk_setup(np, &kirkwood_coreclks); - if (cgnp) + if (cgnp) { mvebu_clk_gating_setup(cgnp, kirkwood_gating_desc); + kirkwood_clk_muxing_setup(cgnp, kirkwood_mux_desc); + } } CLK_OF_DECLARE(kirkwood_clk, "marvell,kirkwood-core-clock", kirkwood_clk_init); diff --git a/drivers/clk/pxa/Makefile b/drivers/clk/pxa/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..4ff2abcd500b8e2109241b56152eeef6dd46f0e0 --- /dev/null +++ b/drivers/clk/pxa/Makefile @@ -0,0 +1,2 @@ +obj-y += clk-pxa.o +obj-$(CONFIG_PXA27x) += clk-pxa27x.o diff --git a/drivers/clk/pxa/clk-pxa.c b/drivers/clk/pxa/clk-pxa.c new file mode 100644 index 0000000000000000000000000000000000000000..ef3c05389c0aeb4704a6f2c2e23720eda6311a83 --- /dev/null +++ b/drivers/clk/pxa/clk-pxa.c @@ -0,0 +1,97 @@ +/* + * Marvell PXA family clocks + * + * Copyright (C) 2014 Robert Jarzmik + * + * Common clock code for PXA clocks ("CKEN" type clocks + DT) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + */ +#include +#include +#include +#include + +#include +#include "clk-pxa.h" + +DEFINE_SPINLOCK(lock); + +static struct clk *pxa_clocks[CLK_MAX]; +static struct clk_onecell_data onecell_data = { + .clks = pxa_clocks, + .clk_num = CLK_MAX, +}; + +#define to_pxa_clk(_hw) container_of(_hw, struct pxa_clk_cken, hw) + +static unsigned long cken_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct pxa_clk_cken *pclk = to_pxa_clk(hw); + struct clk_fixed_factor *fix; + + if (!pclk->is_in_low_power || pclk->is_in_low_power()) + fix = &pclk->lp; + else + fix = &pclk->hp; + fix->hw.clk = hw->clk; + return clk_fixed_factor_ops.recalc_rate(&fix->hw, parent_rate); +} + +static struct clk_ops cken_rate_ops = { + .recalc_rate = cken_recalc_rate, +}; + +static u8 cken_get_parent(struct clk_hw *hw) +{ + struct pxa_clk_cken *pclk = to_pxa_clk(hw); + + if (!pclk->is_in_low_power) + return 0; + return pclk->is_in_low_power() ? 0 : 1; +} + +static struct clk_ops cken_mux_ops = { + .get_parent = cken_get_parent, + .set_parent = dummy_clk_set_parent, +}; + +void __init clkdev_pxa_register(int ckid, const char *con_id, + const char *dev_id, struct clk *clk) +{ + if (!IS_ERR(clk) && (ckid != CLK_NONE)) + pxa_clocks[ckid] = clk; + if (!IS_ERR(clk)) + clk_register_clkdev(clk, con_id, dev_id); +} + +int __init clk_pxa_cken_init(struct pxa_clk_cken *clks, int nb_clks) +{ + int i; + struct pxa_clk_cken *pclk; + struct clk *clk; + + for (i = 0; i < nb_clks; i++) { + pclk = clks + i; + pclk->gate.lock = &lock; + clk = clk_register_composite(NULL, pclk->name, + pclk->parent_names, 2, + &pclk->hw, &cken_mux_ops, + &pclk->hw, &cken_rate_ops, + &pclk->gate.hw, &clk_gate_ops, + pclk->flags); + clkdev_pxa_register(pclk->ckid, pclk->con_id, pclk->dev_id, + clk); + } + return 0; +} + +static void __init pxa_dt_clocks_init(struct device_node *np) +{ + of_clk_add_provider(np, of_clk_src_onecell_get, &onecell_data); +} +CLK_OF_DECLARE(pxa_clks, "marvell,pxa-clocks", pxa_dt_clocks_init); diff --git a/drivers/clk/pxa/clk-pxa.h b/drivers/clk/pxa/clk-pxa.h new file mode 100644 index 0000000000000000000000000000000000000000..5fe219d06b490c49a7203dd547fe65b537089c73 --- /dev/null +++ b/drivers/clk/pxa/clk-pxa.h @@ -0,0 +1,107 @@ +/* + * Marvell PXA family clocks + * + * Copyright (C) 2014 Robert Jarzmik + * + * Common clock code for PXA clocks ("CKEN" type clocks + DT) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + */ +#ifndef _CLK_PXA_ +#define _CLK_PXA_ + +#define PARENTS(name) \ + static const char *name ## _parents[] __initconst +#define MUX_RO_RATE_RO_OPS(name, clk_name) \ + static struct clk_hw name ## _mux_hw; \ + static struct clk_hw name ## _rate_hw; \ + static struct clk_ops name ## _mux_ops = { \ + .get_parent = name ## _get_parent, \ + .set_parent = dummy_clk_set_parent, \ + }; \ + static struct clk_ops name ## _rate_ops = { \ + .recalc_rate = name ## _get_rate, \ + }; \ + static struct clk *clk_register_ ## name(void) \ + { \ + return clk_register_composite(NULL, clk_name, \ + name ## _parents, \ + ARRAY_SIZE(name ## _parents), \ + &name ## _mux_hw, &name ## _mux_ops, \ + &name ## _rate_hw, &name ## _rate_ops, \ + NULL, NULL, CLK_GET_RATE_NOCACHE); \ + } + +#define RATE_RO_OPS(name, clk_name) \ + static struct clk_hw name ## _rate_hw; \ + static struct clk_ops name ## _rate_ops = { \ + .recalc_rate = name ## _get_rate, \ + }; \ + static struct clk *clk_register_ ## name(void) \ + { \ + return clk_register_composite(NULL, clk_name, \ + name ## _parents, \ + ARRAY_SIZE(name ## _parents), \ + NULL, NULL, \ + &name ## _rate_hw, &name ## _rate_ops, \ + NULL, NULL, CLK_GET_RATE_NOCACHE); \ + } + +/* + * CKEN clock type + * This clock takes it source from 2 possible parents : + * - a low power parent + * - a normal parent + * + * +------------+ +-----------+ + * | Low Power | --- | x mult_lp | + * | Clock | | / div_lp |\ + * +------------+ +-----------+ \+-----+ +-----------+ + * | Mux |---| CKEN gate | + * +------------+ +-----------+ /+-----+ +-----------+ + * | High Power | | x mult_hp |/ + * | Clock | --- | / div_hp | + * +------------+ +-----------+ + */ +struct pxa_clk_cken { + struct clk_hw hw; + int ckid; + const char *name; + const char *dev_id; + const char *con_id; + const char **parent_names; + struct clk_fixed_factor lp; + struct clk_fixed_factor hp; + struct clk_gate gate; + bool (*is_in_low_power)(void); + const unsigned long flags; +}; + +#define PXA_CKEN(_dev_id, _con_id, _name, parents, _mult_lp, _div_lp, \ + _mult_hp, _div_hp, is_lp, _cken_reg, _cken_bit, flag) \ + { .ckid = CLK_ ## _name, .name = #_name, \ + .dev_id = _dev_id, .con_id = _con_id, .parent_names = parents,\ + .lp = { .mult = _mult_lp, .div = _div_lp }, \ + .hp = { .mult = _mult_hp, .div = _div_hp }, \ + .is_in_low_power = is_lp, \ + .gate = { .reg = (void __iomem *)_cken_reg, .bit_idx = _cken_bit }, \ + .flags = flag, \ + } +#define PXA_CKEN_1RATE(dev_id, con_id, name, parents, cken_reg, \ + cken_bit, flag) \ + PXA_CKEN(dev_id, con_id, name, parents, 1, 1, 1, 1, \ + NULL, cken_reg, cken_bit, flag) + +static int dummy_clk_set_parent(struct clk_hw *hw, u8 index) +{ + return 0; +} + +extern void clkdev_pxa_register(int ckid, const char *con_id, + const char *dev_id, struct clk *clk); +extern int clk_pxa_cken_init(struct pxa_clk_cken *clks, int nb_clks); + +#endif diff --git a/drivers/clk/pxa/clk-pxa27x.c b/drivers/clk/pxa/clk-pxa27x.c new file mode 100644 index 0000000000000000000000000000000000000000..b345cc791e5defdeeb57d0b8df4d566bd41aef2c --- /dev/null +++ b/drivers/clk/pxa/clk-pxa27x.c @@ -0,0 +1,370 @@ +/* + * Marvell PXA27x family clocks + * + * Copyright (C) 2014 Robert Jarzmik + * + * Heavily inspired from former arch/arm/mach-pxa/clock.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + */ +#include +#include +#include +#include +#include +#include + +#include +#include "clk-pxa.h" + +#define KHz 1000 +#define MHz (1000 * 1000) + +enum { + PXA_CORE_13Mhz = 0, + PXA_CORE_RUN, + PXA_CORE_TURBO, +}; + +enum { + PXA_BUS_13Mhz = 0, + PXA_BUS_RUN, +}; + +enum { + PXA_LCD_13Mhz = 0, + PXA_LCD_RUN, +}; + +enum { + PXA_MEM_13Mhz = 0, + PXA_MEM_SYSTEM_BUS, + PXA_MEM_RUN, +}; + +static const char * const get_freq_khz[] = { + "core", "run", "cpll", "memory", + "system_bus" +}; + +/* + * Get the clock frequency as reflected by CCSR and the turbo flag. + * We assume these values have been applied via a fcs. + * If info is not 0 we also display the current settings. + */ +unsigned int pxa27x_get_clk_frequency_khz(int info) +{ + struct clk *clk; + unsigned long clks[5]; + int i; + + for (i = 0; i < 5; i++) { + clk = clk_get(NULL, get_freq_khz[i]); + if (IS_ERR(clk)) { + clks[i] = 0; + } else { + clks[i] = clk_get_rate(clk); + clk_put(clk); + } + } + if (info) { + pr_info("Run Mode clock: %ld.%02ldMHz\n", + clks[1] / 1000000, (clks[1] % 1000000) / 10000); + pr_info("Turbo Mode clock: %ld.%02ldMHz\n", + clks[2] / 1000000, (clks[2] % 1000000) / 10000); + pr_info("Memory clock: %ld.%02ldMHz\n", + clks[3] / 1000000, (clks[3] % 1000000) / 10000); + pr_info("System bus clock: %ld.%02ldMHz\n", + clks[4] / 1000000, (clks[4] % 1000000) / 10000); + } + return (unsigned int)clks[0]; +} + +bool pxa27x_is_ppll_disabled(void) +{ + unsigned long ccsr = CCSR; + + return ccsr & (1 << CCCR_PPDIS_BIT); +} + +#define PXA27X_CKEN(dev_id, con_id, parents, mult_hp, div_hp, \ + bit, is_lp, flags) \ + PXA_CKEN(dev_id, con_id, bit, parents, 1, 1, mult_hp, div_hp, \ + is_lp, &CKEN, CKEN_ ## bit, flags) +#define PXA27X_PBUS_CKEN(dev_id, con_id, bit, mult_hp, div_hp, delay) \ + PXA27X_CKEN(dev_id, con_id, pxa27x_pbus_parents, mult_hp, \ + div_hp, bit, pxa27x_is_ppll_disabled, 0) + +PARENTS(pxa27x_pbus) = { "osc_13mhz", "ppll_312mhz" }; +PARENTS(pxa27x_sbus) = { "system_bus", "system_bus" }; +PARENTS(pxa27x_32Mhz_bus) = { "osc_32_768khz", "osc_32_768khz" }; +PARENTS(pxa27x_lcd_bus) = { "lcd_base", "lcd_base" }; +PARENTS(pxa27x_membus) = { "lcd_base", "lcd_base" }; + +#define PXA27X_CKEN_1RATE(dev_id, con_id, bit, parents, delay) \ + PXA_CKEN_1RATE(dev_id, con_id, bit, parents, \ + &CKEN, CKEN_ ## bit, 0) +#define PXA27X_CKEN_1RATE_AO(dev_id, con_id, bit, parents, delay) \ + PXA_CKEN_1RATE(dev_id, con_id, bit, parents, \ + &CKEN, CKEN_ ## bit, CLK_IGNORE_UNUSED) + +static struct pxa_clk_cken pxa27x_clocks[] = { + PXA27X_PBUS_CKEN("pxa2xx-uart.0", NULL, FFUART, 2, 42, 1), + PXA27X_PBUS_CKEN("pxa2xx-uart.1", NULL, BTUART, 2, 42, 1), + PXA27X_PBUS_CKEN("pxa2xx-uart.2", NULL, STUART, 2, 42, 1), + PXA27X_PBUS_CKEN("pxa2xx-i2s", NULL, I2S, 2, 51, 0), + PXA27X_PBUS_CKEN("pxa2xx-i2c.0", NULL, I2C, 2, 19, 0), + PXA27X_PBUS_CKEN("pxa27x-udc", NULL, USB, 2, 13, 5), + PXA27X_PBUS_CKEN("pxa2xx-mci.0", NULL, MMC, 2, 32, 0), + PXA27X_PBUS_CKEN("pxa2xx-ir", "FICPCLK", FICP, 2, 13, 0), + PXA27X_PBUS_CKEN("pxa27x-ohci", NULL, USBHOST, 2, 13, 0), + PXA27X_PBUS_CKEN("pxa2xx-i2c.1", NULL, PWRI2C, 1, 24, 0), + PXA27X_PBUS_CKEN("pxa27x-ssp.0", NULL, SSP1, 1, 24, 0), + PXA27X_PBUS_CKEN("pxa27x-ssp.1", NULL, SSP2, 1, 24, 0), + PXA27X_PBUS_CKEN("pxa27x-ssp.2", NULL, SSP3, 1, 24, 0), + PXA27X_PBUS_CKEN("pxa27x-pwm.0", NULL, PWM0, 1, 24, 0), + PXA27X_PBUS_CKEN("pxa27x-pwm.1", NULL, PWM1, 1, 24, 0), + PXA27X_PBUS_CKEN(NULL, "MSLCLK", MSL, 2, 13, 0), + PXA27X_PBUS_CKEN(NULL, "USIMCLK", USIM, 2, 13, 0), + PXA27X_PBUS_CKEN(NULL, "MSTKCLK", MEMSTK, 2, 32, 0), + PXA27X_PBUS_CKEN(NULL, "AC97CLK", AC97, 1, 1, 0), + PXA27X_PBUS_CKEN(NULL, "AC97CONFCLK", AC97CONF, 1, 1, 0), + PXA27X_PBUS_CKEN(NULL, "OSTIMER0", OSTIMER, 1, 96, 0), + + PXA27X_CKEN_1RATE("pxa27x-keypad", NULL, KEYPAD, + pxa27x_32Mhz_bus_parents, 0), + PXA27X_CKEN_1RATE(NULL, "IMCLK", IM, pxa27x_sbus_parents, 0), + PXA27X_CKEN_1RATE("pxa2xx-fb", NULL, LCD, pxa27x_lcd_bus_parents, 0), + PXA27X_CKEN_1RATE("pxa27x-camera.0", NULL, CAMERA, + pxa27x_lcd_bus_parents, 0), + PXA27X_CKEN_1RATE_AO("pxa2xx-pcmcia", NULL, MEMC, + pxa27x_membus_parents, 0), + +}; + +static unsigned long clk_pxa27x_cpll_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned long clkcfg; + unsigned int t, ht; + unsigned int l, L, n2, N; + unsigned long ccsr = CCSR; + + asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); + t = clkcfg & (1 << 0); + ht = clkcfg & (1 << 2); + + l = ccsr & CCSR_L_MASK; + n2 = (ccsr & CCSR_N2_MASK) >> CCSR_N2_SHIFT; + L = l * parent_rate; + N = (L * n2) / 2; + + return t ? N : L; +} +PARENTS(clk_pxa27x_cpll) = { "osc_13mhz" }; +RATE_RO_OPS(clk_pxa27x_cpll, "cpll"); + +static unsigned long clk_pxa27x_lcd_base_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned int l, osc_forced; + unsigned long ccsr = CCSR; + unsigned long cccr = CCCR; + + l = ccsr & CCSR_L_MASK; + osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); + if (osc_forced) { + if (cccr & (1 << CCCR_LCD_26_BIT)) + return parent_rate * 2; + else + return parent_rate; + } + + if (l <= 7) + return parent_rate; + if (l <= 16) + return parent_rate / 2; + return parent_rate / 4; +} + +static u8 clk_pxa27x_lcd_base_get_parent(struct clk_hw *hw) +{ + unsigned int osc_forced; + unsigned long ccsr = CCSR; + + osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); + if (osc_forced) + return PXA_LCD_13Mhz; + else + return PXA_LCD_RUN; +} + +PARENTS(clk_pxa27x_lcd_base) = { "osc_13mhz", "run" }; +MUX_RO_RATE_RO_OPS(clk_pxa27x_lcd_base, "lcd_base"); + +static void __init pxa27x_register_plls(void) +{ + clk_register_fixed_rate(NULL, "osc_13mhz", NULL, + CLK_GET_RATE_NOCACHE | CLK_IS_ROOT, + 13 * MHz); + clk_register_fixed_rate(NULL, "osc_32_768khz", NULL, + CLK_GET_RATE_NOCACHE | CLK_IS_ROOT, + 32768 * KHz); + clk_register_fixed_rate(NULL, "clk_dummy", NULL, CLK_IS_ROOT, 0); + clk_register_fixed_factor(NULL, "ppll_312mhz", "osc_13mhz", 0, 24, 1); +} + +static unsigned long clk_pxa27x_core_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned long clkcfg; + unsigned int t, ht, b, osc_forced; + unsigned long ccsr = CCSR; + + osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); + asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); + t = clkcfg & (1 << 0); + ht = clkcfg & (1 << 2); + b = clkcfg & (1 << 3); + + if (osc_forced) + return parent_rate; + if (ht) + return parent_rate / 2; + else + return parent_rate; +} + +static u8 clk_pxa27x_core_get_parent(struct clk_hw *hw) +{ + unsigned long clkcfg; + unsigned int t, ht, b, osc_forced; + unsigned long ccsr = CCSR; + + osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); + if (osc_forced) + return PXA_CORE_13Mhz; + + asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); + t = clkcfg & (1 << 0); + ht = clkcfg & (1 << 2); + b = clkcfg & (1 << 3); + + if (ht || t) + return PXA_CORE_TURBO; + return PXA_CORE_RUN; +} +PARENTS(clk_pxa27x_core) = { "osc_13mhz", "run", "cpll" }; +MUX_RO_RATE_RO_OPS(clk_pxa27x_core, "core"); + +static unsigned long clk_pxa27x_run_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned long ccsr = CCSR; + unsigned int n2 = (ccsr & CCSR_N2_MASK) >> CCSR_N2_SHIFT; + + return (parent_rate / n2) * 2; +} +PARENTS(clk_pxa27x_run) = { "cpll" }; +RATE_RO_OPS(clk_pxa27x_run, "run"); + +static void __init pxa27x_register_core(void) +{ + clk_register_clk_pxa27x_cpll(); + clk_register_clk_pxa27x_run(); + + clkdev_pxa_register(CLK_CORE, "core", NULL, + clk_register_clk_pxa27x_core()); +} + +static unsigned long clk_pxa27x_system_bus_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned long clkcfg; + unsigned int b, osc_forced; + unsigned long ccsr = CCSR; + + osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); + asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); + b = clkcfg & (1 << 3); + + if (osc_forced) + return parent_rate; + if (b) + return parent_rate / 2; + else + return parent_rate; +} + +static u8 clk_pxa27x_system_bus_get_parent(struct clk_hw *hw) +{ + unsigned int osc_forced; + unsigned long ccsr = CCSR; + + osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); + if (osc_forced) + return PXA_BUS_13Mhz; + else + return PXA_BUS_RUN; +} + +PARENTS(clk_pxa27x_system_bus) = { "osc_13mhz", "run" }; +MUX_RO_RATE_RO_OPS(clk_pxa27x_system_bus, "system_bus"); + +static unsigned long clk_pxa27x_memory_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned int a, l, osc_forced; + unsigned long cccr = CCCR; + unsigned long ccsr = CCSR; + + osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); + a = cccr & CCCR_A_BIT; + l = ccsr & CCSR_L_MASK; + + if (osc_forced || a) + return parent_rate; + if (l <= 10) + return parent_rate; + if (l <= 20) + return parent_rate / 2; + return parent_rate / 4; +} + +static u8 clk_pxa27x_memory_get_parent(struct clk_hw *hw) +{ + unsigned int osc_forced, a; + unsigned long cccr = CCCR; + unsigned long ccsr = CCSR; + + osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); + a = cccr & CCCR_A_BIT; + if (osc_forced) + return PXA_MEM_13Mhz; + if (a) + return PXA_MEM_SYSTEM_BUS; + else + return PXA_MEM_RUN; +} + +PARENTS(clk_pxa27x_memory) = { "osc_13mhz", "system_bus", "run" }; +MUX_RO_RATE_RO_OPS(clk_pxa27x_memory, "memory"); + +static void __init pxa27x_base_clocks_init(void) +{ + pxa27x_register_plls(); + pxa27x_register_core(); + clk_register_clk_pxa27x_system_bus(); + clk_register_clk_pxa27x_memory(); + clk_register_clk_pxa27x_lcd_base(); +} + +static int __init pxa27x_clocks_init(void) +{ + pxa27x_base_clocks_init(); + return clk_pxa_cken_init(pxa27x_clocks, ARRAY_SIZE(pxa27x_clocks)); +} +postcore_initcall(pxa27x_clocks_init); diff --git a/drivers/clk/qcom/clk-pll.c b/drivers/clk/qcom/clk-pll.c index 9db03d3b1657302b5b4ef8e5cca4b19a87edd7e3..b823bc3b625067c3b64b32b79a0b8b3e17541096 100644 --- a/drivers/clk/qcom/clk-pll.c +++ b/drivers/clk/qcom/clk-pll.c @@ -97,7 +97,7 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_pll *pll = to_clk_pll(hw); - u32 l, m, n; + u32 l, m, n, config; unsigned long rate; u64 tmp; @@ -116,13 +116,79 @@ clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) do_div(tmp, n); rate += tmp; } + if (pll->post_div_width) { + regmap_read(pll->clkr.regmap, pll->config_reg, &config); + config >>= pll->post_div_shift; + config &= BIT(pll->post_div_width) - 1; + rate /= config + 1; + } + return rate; } +static const +struct pll_freq_tbl *find_freq(const struct pll_freq_tbl *f, unsigned long rate) +{ + if (!f) + return NULL; + + for (; f->freq; f++) + if (rate <= f->freq) + return f; + + return NULL; +} + +static long +clk_pll_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *p_rate, struct clk **p) +{ + struct clk_pll *pll = to_clk_pll(hw); + const struct pll_freq_tbl *f; + + f = find_freq(pll->freq_tbl, rate); + if (!f) + return clk_pll_recalc_rate(hw, *p_rate); + + return f->freq; +} + +static int +clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long p_rate) +{ + struct clk_pll *pll = to_clk_pll(hw); + const struct pll_freq_tbl *f; + bool enabled; + u32 mode; + u32 enable_mask = PLL_OUTCTRL | PLL_BYPASSNL | PLL_RESET_N; + + f = find_freq(pll->freq_tbl, rate); + if (!f) + return -EINVAL; + + regmap_read(pll->clkr.regmap, pll->mode_reg, &mode); + enabled = (mode & enable_mask) == enable_mask; + + if (enabled) + clk_pll_disable(hw); + + regmap_update_bits(pll->clkr.regmap, pll->l_reg, 0x3ff, f->l); + regmap_update_bits(pll->clkr.regmap, pll->m_reg, 0x7ffff, f->m); + regmap_update_bits(pll->clkr.regmap, pll->n_reg, 0x7ffff, f->n); + regmap_write(pll->clkr.regmap, pll->config_reg, f->ibits); + + if (enabled) + clk_pll_enable(hw); + + return 0; +} + const struct clk_ops clk_pll_ops = { .enable = clk_pll_enable, .disable = clk_pll_disable, .recalc_rate = clk_pll_recalc_rate, + .determine_rate = clk_pll_determine_rate, + .set_rate = clk_pll_set_rate, }; EXPORT_SYMBOL_GPL(clk_pll_ops); diff --git a/drivers/clk/qcom/clk-pll.h b/drivers/clk/qcom/clk-pll.h index 3003e9962472f3ae98ae55709d2d4f347d2ccf40..c9c0cda306d04e45548daa0d5b50f802ab2c04a9 100644 --- a/drivers/clk/qcom/clk-pll.h +++ b/drivers/clk/qcom/clk-pll.h @@ -17,6 +17,21 @@ #include #include "clk-regmap.h" +/** + * struct pll_freq_tbl - PLL frequency table + * @l: L value + * @m: M value + * @n: N value + * @ibits: internal values + */ +struct pll_freq_tbl { + unsigned long freq; + u16 l; + u16 m; + u16 n; + u32 ibits; +}; + /** * struct clk_pll - phase locked loop (PLL) * @l_reg: L register @@ -26,6 +41,7 @@ * @mode_reg: mode register * @status_reg: status register * @status_bit: ANDed with @status_reg to determine if PLL is enabled + * @freq_tbl: PLL frequency table * @hw: handle between common and hardware-specific interfaces */ struct clk_pll { @@ -36,6 +52,10 @@ struct clk_pll { u32 mode_reg; u32 status_reg; u8 status_bit; + u8 post_div_width; + u8 post_div_shift; + + const struct pll_freq_tbl *freq_tbl; struct clk_regmap clkr; }; diff --git a/drivers/clk/qcom/clk-rcg.c b/drivers/clk/qcom/clk-rcg.c index b638c5846dbfb20e2cc20046afe9823927713921..b6e6959e89aafed9248ba6ddd882e0ece7a4deb1 100644 --- a/drivers/clk/qcom/clk-rcg.c +++ b/drivers/clk/qcom/clk-rcg.c @@ -21,6 +21,7 @@ #include #include "clk-rcg.h" +#include "common.h" static u32 ns_to_src(struct src_sel *s, u32 ns) { @@ -67,16 +68,16 @@ static u8 clk_dyn_rcg_get_parent(struct clk_hw *hw) { struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw); int num_parents = __clk_get_num_parents(hw->clk); - u32 ns, ctl; + u32 ns, reg; int bank; int i; struct src_sel *s; - regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl); - bank = reg_to_bank(rcg, ctl); + regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); + bank = reg_to_bank(rcg, reg); s = &rcg->s[bank]; - regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); + regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns); ns = ns_to_src(s, ns); for (i = 0; i < num_parents; i++) @@ -192,90 +193,93 @@ static u32 mn_to_reg(struct mn *mn, u32 m, u32 n, u32 val) static void configure_bank(struct clk_dyn_rcg *rcg, const struct freq_tbl *f) { - u32 ns, md, ctl, *regp; + u32 ns, md, reg; int bank, new_bank; struct mn *mn; struct pre_div *p; struct src_sel *s; bool enabled; - u32 md_reg; - u32 bank_reg; + u32 md_reg, ns_reg; bool banked_mn = !!rcg->mn[1].width; + bool banked_p = !!rcg->p[1].pre_div_width; struct clk_hw *hw = &rcg->clkr.hw; enabled = __clk_is_enabled(hw->clk); - regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); - regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl); - - if (banked_mn) { - regp = &ctl; - bank_reg = rcg->clkr.enable_reg; - } else { - regp = &ns; - bank_reg = rcg->ns_reg; - } - - bank = reg_to_bank(rcg, *regp); + regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); + bank = reg_to_bank(rcg, reg); new_bank = enabled ? !bank : bank; + ns_reg = rcg->ns_reg[new_bank]; + regmap_read(rcg->clkr.regmap, ns_reg, &ns); + if (banked_mn) { mn = &rcg->mn[new_bank]; md_reg = rcg->md_reg[new_bank]; ns |= BIT(mn->mnctr_reset_bit); - regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); + regmap_write(rcg->clkr.regmap, ns_reg, ns); regmap_read(rcg->clkr.regmap, md_reg, &md); md = mn_to_md(mn, f->m, f->n, md); regmap_write(rcg->clkr.regmap, md_reg, md); ns = mn_to_ns(mn, f->m, f->n, ns); - regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); + regmap_write(rcg->clkr.regmap, ns_reg, ns); - ctl = mn_to_reg(mn, f->m, f->n, ctl); - regmap_write(rcg->clkr.regmap, rcg->clkr.enable_reg, ctl); + /* Two NS registers means mode control is in NS register */ + if (rcg->ns_reg[0] != rcg->ns_reg[1]) { + ns = mn_to_reg(mn, f->m, f->n, ns); + regmap_write(rcg->clkr.regmap, ns_reg, ns); + } else { + reg = mn_to_reg(mn, f->m, f->n, reg); + regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg); + } ns &= ~BIT(mn->mnctr_reset_bit); - regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); - } else { + regmap_write(rcg->clkr.regmap, ns_reg, ns); + } + + if (banked_p) { p = &rcg->p[new_bank]; ns = pre_div_to_ns(p, f->pre_div - 1, ns); } s = &rcg->s[new_bank]; ns = src_to_ns(s, s->parent_map[f->src], ns); - regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); + regmap_write(rcg->clkr.regmap, ns_reg, ns); if (enabled) { - *regp ^= BIT(rcg->mux_sel_bit); - regmap_write(rcg->clkr.regmap, bank_reg, *regp); + regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); + reg ^= BIT(rcg->mux_sel_bit); + regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg); } } static int clk_dyn_rcg_set_parent(struct clk_hw *hw, u8 index) { struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw); - u32 ns, ctl, md, reg; + u32 ns, md, reg; int bank; struct freq_tbl f = { 0 }; bool banked_mn = !!rcg->mn[1].width; + bool banked_p = !!rcg->p[1].pre_div_width; - regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); - regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl); - reg = banked_mn ? ctl : ns; - + regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); bank = reg_to_bank(rcg, reg); + regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns); + if (banked_mn) { regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md); f.m = md_to_m(&rcg->mn[bank], md); f.n = ns_m_to_n(&rcg->mn[bank], ns, f.m); - } else { - f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1; } - f.src = index; + if (banked_p) + f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1; + + f.src = index; configure_bank(rcg, &f); return 0; @@ -336,41 +340,30 @@ clk_dyn_rcg_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) u32 m, n, pre_div, ns, md, mode, reg; int bank; struct mn *mn; + bool banked_p = !!rcg->p[1].pre_div_width; bool banked_mn = !!rcg->mn[1].width; - regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); - - if (banked_mn) - regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, ®); - else - reg = ns; - + regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); bank = reg_to_bank(rcg, reg); + regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns); + m = n = pre_div = mode = 0; + if (banked_mn) { mn = &rcg->mn[bank]; regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md); m = md_to_m(mn, md); n = ns_m_to_n(mn, ns, m); + /* Two NS registers means mode control is in NS register */ + if (rcg->ns_reg[0] != rcg->ns_reg[1]) + reg = ns; mode = reg_to_mnctr_mode(mn, reg); - return calc_rate(parent_rate, m, n, mode, 0); - } else { - pre_div = ns_to_pre_div(&rcg->p[bank], ns); - return calc_rate(parent_rate, 0, 0, 0, pre_div); } -} -static const -struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate) -{ - if (!f) - return NULL; - - for (; f->freq; f++) - if (rate <= f->freq) - return f; + if (banked_p) + pre_div = ns_to_pre_div(&rcg->p[bank], ns); - return NULL; + return calc_rate(parent_rate, m, n, mode, pre_div); } static long _freq_tbl_determine_rate(struct clk_hw *hw, @@ -379,7 +372,7 @@ static long _freq_tbl_determine_rate(struct clk_hw *hw, { unsigned long clk_flags; - f = find_freq(f, rate); + f = qcom_find_freq(f, rate); if (!f) return -EINVAL; @@ -477,7 +470,7 @@ static int clk_rcg_set_rate(struct clk_hw *hw, unsigned long rate, struct clk_rcg *rcg = to_clk_rcg(hw); const struct freq_tbl *f; - f = find_freq(rcg->freq_tbl, rate); + f = qcom_find_freq(rcg->freq_tbl, rate); if (!f) return -EINVAL; @@ -497,7 +490,7 @@ static int __clk_dyn_rcg_set_rate(struct clk_hw *hw, unsigned long rate) struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw); const struct freq_tbl *f; - f = find_freq(rcg->freq_tbl, rate); + f = qcom_find_freq(rcg->freq_tbl, rate); if (!f) return -EINVAL; diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index ba0523cefd2e4ad04fcb6a853e727263011333ee..687e41f91d7c9f90d99eb80acbd98086d237e4e5 100644 --- a/drivers/clk/qcom/clk-rcg.h +++ b/drivers/clk/qcom/clk-rcg.h @@ -103,8 +103,9 @@ extern const struct clk_ops clk_rcg_bypass_ops; * struct clk_dyn_rcg - root clock generator with glitch free mux * * @mux_sel_bit: bit to switch glitch free mux - * @ns_reg: NS register + * @ns_reg: NS0 and NS1 register * @md_reg: MD0 and MD1 register + * @bank_reg: register to XOR @mux_sel_bit into to switch glitch free mux * @mn: mn counter (banked) * @s: source selector (banked) * @freq_tbl: frequency table @@ -113,8 +114,9 @@ extern const struct clk_ops clk_rcg_bypass_ops; * */ struct clk_dyn_rcg { - u32 ns_reg; + u32 ns_reg[2]; u32 md_reg[2]; + u32 bank_reg; u8 mux_sel_bit; diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index cd185d5cc67a3710f4a825c7ffd8bf4f66ae7f45..cfa9eb4fe9ca60c1420e7821f7d927e1fc82fb10 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -24,6 +24,7 @@ #include #include "clk-rcg.h" +#include "common.h" #define CMD_REG 0x0 #define CMD_UPDATE BIT(0) @@ -172,27 +173,13 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) return calc_rate(parent_rate, m, n, mode, hid_div); } -static const -struct freq_tbl *find_freq(const struct freq_tbl *f, unsigned long rate) -{ - if (!f) - return NULL; - - for (; f->freq; f++) - if (rate <= f->freq) - return f; - - /* Default to our fastest rate */ - return f - 1; -} - static long _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f, unsigned long rate, unsigned long *p_rate, struct clk **p) { unsigned long clk_flags; - f = find_freq(f, rate); + f = qcom_find_freq(f, rate); if (!f) return -EINVAL; @@ -268,7 +255,7 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) struct clk_rcg2 *rcg = to_clk_rcg2(hw); const struct freq_tbl *f; - f = find_freq(rcg->freq_tbl, rate); + f = qcom_find_freq(rcg->freq_tbl, rate); if (!f) return -EINVAL; diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index eeb3eea01f4ca751944df67da5d4df65cabbb048..e20d947db3e50516a132f9e6cc17717da0abd23f 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -18,6 +18,7 @@ #include #include "common.h" +#include "clk-rcg.h" #include "clk-regmap.h" #include "reset.h" @@ -27,6 +28,21 @@ struct qcom_cc { struct clk *clks[]; }; +const +struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate) +{ + if (!f) + return NULL; + + for (; f->freq; f++) + if (rate <= f->freq) + return f; + + /* Default to our fastest rate */ + return f - 1; +} +EXPORT_SYMBOL_GPL(qcom_find_freq); + struct regmap * qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc) { diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index 2765e9d3da97ef5c23f976dd0e62fa4c34edf1d5..f519322acdf3be21d4ada6d014a517b459f956a6 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -18,6 +18,7 @@ struct regmap_config; struct clk_regmap; struct qcom_reset_map; struct regmap; +struct freq_tbl; struct qcom_cc_desc { const struct regmap_config *config; @@ -27,6 +28,9 @@ struct qcom_cc_desc { size_t num_resets; }; +extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, + unsigned long rate); + extern struct regmap *qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc); extern int qcom_cc_really_probe(struct platform_device *pdev, diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c index 3b83b7dd78c7297b612c2c3720e8c86568ce447e..5cd62a709ac7d5baa494865b6a63aa2d533eb516 100644 --- a/drivers/clk/qcom/gcc-ipq806x.c +++ b/drivers/clk/qcom/gcc-ipq806x.c @@ -32,6 +32,33 @@ #include "clk-branch.h" #include "reset.h" +static struct clk_pll pll0 = { + .l_reg = 0x30c4, + .m_reg = 0x30c8, + .n_reg = 0x30cc, + .config_reg = 0x30d4, + .mode_reg = 0x30c0, + .status_reg = 0x30d8, + .status_bit = 16, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pll0", + .parent_names = (const char *[]){ "pxo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +static struct clk_regmap pll0_vote = { + .enable_reg = 0x34c0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "pll0_vote", + .parent_names = (const char *[]){ "pll0" }, + .num_parents = 1, + .ops = &clk_pll_vote_ops, + }, +}; + static struct clk_pll pll3 = { .l_reg = 0x3164, .m_reg = 0x3168, @@ -154,7 +181,7 @@ static const u8 gcc_pxo_pll8_pll0[] = { static const char *gcc_pxo_pll8_pll0_map[] = { "pxo", "pll8_vote", - "pll0", + "pll0_vote", }; static struct freq_tbl clk_tbl_gsbi_uart[] = { @@ -2133,6 +2160,8 @@ static struct clk_branch usb_fs1_h_clk = { }; static struct clk_regmap *gcc_ipq806x_clks[] = { + [PLL0] = &pll0.clkr, + [PLL0_VOTE] = &pll0_vote, [PLL3] = &pll3.clkr, [PLL8] = &pll8.clkr, [PLL8_VOTE] = &pll8_vote, diff --git a/drivers/clk/qcom/mmcc-apq8084.c b/drivers/clk/qcom/mmcc-apq8084.c index 751eea376a2b078a7b8221608a31cf95b92d3a96..dab988ab8cf12740ac931c5f5efaa39b90887ec3 100644 --- a/drivers/clk/qcom/mmcc-apq8084.c +++ b/drivers/clk/qcom/mmcc-apq8084.c @@ -3341,7 +3341,6 @@ static struct platform_driver mmcc_apq8084_driver = { .remove = mmcc_apq8084_remove, .driver = { .name = "mmcc-apq8084", - .owner = THIS_MODULE, .of_match_table = mmcc_apq8084_match_table, }, }; diff --git a/drivers/clk/qcom/mmcc-msm8960.c b/drivers/clk/qcom/mmcc-msm8960.c index 2e80a219b8ead315a053676f1776c39363f12fda..e8b33bbc362f9aa552959fbd56d231dba86ee1f0 100644 --- a/drivers/clk/qcom/mmcc-msm8960.c +++ b/drivers/clk/qcom/mmcc-msm8960.c @@ -773,9 +773,11 @@ static struct freq_tbl clk_tbl_gfx2d[] = { }; static struct clk_dyn_rcg gfx2d0_src = { - .ns_reg = 0x0070, + .ns_reg[0] = 0x0070, + .ns_reg[1] = 0x0070, .md_reg[0] = 0x0064, .md_reg[1] = 0x0068, + .bank_reg = 0x0060, .mn[0] = { .mnctr_en_bit = 8, .mnctr_reset_bit = 25, @@ -831,9 +833,11 @@ static struct clk_branch gfx2d0_clk = { }; static struct clk_dyn_rcg gfx2d1_src = { - .ns_reg = 0x007c, + .ns_reg[0] = 0x007c, + .ns_reg[1] = 0x007c, .md_reg[0] = 0x0078, .md_reg[1] = 0x006c, + .bank_reg = 0x0074, .mn[0] = { .mnctr_en_bit = 8, .mnctr_reset_bit = 25, @@ -930,9 +934,11 @@ static struct freq_tbl clk_tbl_gfx3d_8064[] = { }; static struct clk_dyn_rcg gfx3d_src = { - .ns_reg = 0x008c, + .ns_reg[0] = 0x008c, + .ns_reg[1] = 0x008c, .md_reg[0] = 0x0084, .md_reg[1] = 0x0088, + .bank_reg = 0x0080, .mn[0] = { .mnctr_en_bit = 8, .mnctr_reset_bit = 25, @@ -1006,9 +1012,11 @@ static struct freq_tbl clk_tbl_vcap[] = { }; static struct clk_dyn_rcg vcap_src = { - .ns_reg = 0x021c, + .ns_reg[0] = 0x021c, + .ns_reg[1] = 0x021c, .md_reg[0] = 0x01ec, .md_reg[1] = 0x0218, + .bank_reg = 0x0178, .mn[0] = { .mnctr_en_bit = 8, .mnctr_reset_bit = 23, @@ -1211,9 +1219,11 @@ static struct freq_tbl clk_tbl_mdp[] = { }; static struct clk_dyn_rcg mdp_src = { - .ns_reg = 0x00d0, + .ns_reg[0] = 0x00d0, + .ns_reg[1] = 0x00d0, .md_reg[0] = 0x00c4, .md_reg[1] = 0x00c8, + .bank_reg = 0x00c0, .mn[0] = { .mnctr_en_bit = 8, .mnctr_reset_bit = 31, @@ -1318,7 +1328,9 @@ static struct freq_tbl clk_tbl_rot[] = { }; static struct clk_dyn_rcg rot_src = { - .ns_reg = 0x00e8, + .ns_reg[0] = 0x00e8, + .ns_reg[1] = 0x00e8, + .bank_reg = 0x00e8, .p[0] = { .pre_div_shift = 22, .pre_div_width = 4, @@ -1542,9 +1554,11 @@ static struct freq_tbl clk_tbl_vcodec[] = { }; static struct clk_dyn_rcg vcodec_src = { - .ns_reg = 0x0100, + .ns_reg[0] = 0x0100, + .ns_reg[1] = 0x0100, .md_reg[0] = 0x00fc, .md_reg[1] = 0x0128, + .bank_reg = 0x00f8, .mn[0] = { .mnctr_en_bit = 5, .mnctr_reset_bit = 31, @@ -2679,7 +2693,6 @@ static struct platform_driver mmcc_msm8960_driver = { .remove = mmcc_msm8960_remove, .driver = { .name = "mmcc-msm8960", - .owner = THIS_MODULE, .of_match_table = mmcc_msm8960_match_table, }, }; diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c index bc8f519c47aad0054f675f6595f436f9a1363f6c..be94c54a9a4f72db338c07f260a69da0b706b561 100644 --- a/drivers/clk/qcom/mmcc-msm8974.c +++ b/drivers/clk/qcom/mmcc-msm8974.c @@ -2570,7 +2570,6 @@ static struct platform_driver mmcc_msm8974_driver = { .remove = mmcc_msm8974_remove, .driver = { .name = "mmcc-msm8974", - .owner = THIS_MODULE, .of_match_table = mmcc_msm8974_match_table, }, }; diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index ee6b077381e11193840e848ec822a86602c42413..bd8514d63634bdb78ae0e75f801be478265f5f91 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -5,6 +5,7 @@ obj-y += clk-rockchip.o obj-y += clk.o obj-y += clk-pll.o +obj-y += clk-cpu.o obj-$(CONFIG_RESET_CONTROLLER) += softrst.o obj-y += clk-rk3188.o diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c new file mode 100644 index 0000000000000000000000000000000000000000..75c8c45ef72849358e4b7bf2c3bbff7927761ae0 --- /dev/null +++ b/drivers/clk/rockchip/clk-cpu.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2014 MundoReader S.L. + * Author: Heiko Stuebner + * + * based on clk/samsung/clk-cpu.c + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Author: Thomas Abraham + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * A CPU clock is defined as a clock supplied to a CPU or a group of CPUs. + * The CPU clock is typically derived from a hierarchy of clock + * blocks which includes mux and divider blocks. There are a number of other + * auxiliary clocks supplied to the CPU domain such as the debug blocks and AXI + * clock for CPU domain. The rates of these auxiliary clocks are related to the + * CPU clock rate and this relation is usually specified in the hardware manual + * of the SoC or supplied after the SoC characterization. + * + * The below implementation of the CPU clock allows the rate changes of the CPU + * clock and the corresponding rate changes of the auxillary clocks of the CPU + * domain. The platform clock driver provides a clock register configuration + * for each configurable rate which is then used to program the clock hardware + * registers to acheive a fast co-oridinated rate change for all the CPU domain + * clocks. + * + * On a rate change request for the CPU clock, the rate change is propagated + * upto the PLL supplying the clock to the CPU domain clock blocks. While the + * CPU domain PLL is reconfigured, the CPU domain clocks are driven using an + * alternate clock source. If required, the alternate clock source is divided + * down in order to keep the output clock rate within the previous OPP limits. + */ + +#include +#include +#include +#include +#include "clk.h" + +/** + * struct rockchip_cpuclk: information about clock supplied to a CPU core. + * @hw: handle between ccf and cpu clock. + * @alt_parent: alternate parent clock to use when switching the speed + * of the primary parent clock. + * @reg_base: base register for cpu-clock values. + * @clk_nb: clock notifier registered for changes in clock speed of the + * primary parent clock. + * @rate_count: number of rates in the rate_table + * @rate_table: pll-rates and their associated dividers + * @reg_data: cpu-specific register settings + * @lock: clock lock + */ +struct rockchip_cpuclk { + struct clk_hw hw; + + struct clk_mux cpu_mux; + const struct clk_ops *cpu_mux_ops; + + struct clk *alt_parent; + void __iomem *reg_base; + struct notifier_block clk_nb; + unsigned int rate_count; + struct rockchip_cpuclk_rate_table *rate_table; + const struct rockchip_cpuclk_reg_data *reg_data; + spinlock_t *lock; +}; + +#define to_rockchip_cpuclk_hw(hw) container_of(hw, struct rockchip_cpuclk, hw) +#define to_rockchip_cpuclk_nb(nb) \ + container_of(nb, struct rockchip_cpuclk, clk_nb) + +static const struct rockchip_cpuclk_rate_table *rockchip_get_cpuclk_settings( + struct rockchip_cpuclk *cpuclk, unsigned long rate) +{ + const struct rockchip_cpuclk_rate_table *rate_table = + cpuclk->rate_table; + int i; + + for (i = 0; i < cpuclk->rate_count; i++) { + if (rate == rate_table[i].prate) + return &rate_table[i]; + } + + return NULL; +} + +static unsigned long rockchip_cpuclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_hw(hw); + const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data; + u32 clksel0 = readl_relaxed(cpuclk->reg_base + reg_data->core_reg); + + clksel0 >>= reg_data->div_core_shift; + clksel0 &= reg_data->div_core_mask; + return parent_rate / (clksel0 + 1); +} + +static const struct clk_ops rockchip_cpuclk_ops = { + .recalc_rate = rockchip_cpuclk_recalc_rate, +}; + +static void rockchip_cpuclk_set_dividers(struct rockchip_cpuclk *cpuclk, + const struct rockchip_cpuclk_rate_table *rate) +{ + int i; + + /* alternate parent is active now. set the dividers */ + for (i = 0; i < ARRAY_SIZE(rate->divs); i++) { + const struct rockchip_cpuclk_clksel *clksel = &rate->divs[i]; + + if (!clksel->reg) + continue; + + pr_debug("%s: setting reg 0x%x to 0x%x\n", + __func__, clksel->reg, clksel->val); + writel(clksel->val , cpuclk->reg_base + clksel->reg); + } +} + +static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk, + struct clk_notifier_data *ndata) +{ + const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data; + unsigned long alt_prate, alt_div; + + alt_prate = clk_get_rate(cpuclk->alt_parent); + + spin_lock(cpuclk->lock); + + /* + * If the old parent clock speed is less than the clock speed + * of the alternate parent, then it should be ensured that at no point + * the armclk speed is more than the old_rate until the dividers are + * set. + */ + if (alt_prate > ndata->old_rate) { + /* calculate dividers */ + alt_div = DIV_ROUND_UP(alt_prate, ndata->old_rate) - 1; + if (alt_div > reg_data->div_core_mask) { + pr_warn("%s: limiting alt-divider %lu to %d\n", + __func__, alt_div, reg_data->div_core_mask); + alt_div = reg_data->div_core_mask; + } + + /* + * Change parents and add dividers in a single transaction. + * + * NOTE: we do this in a single transaction so we're never + * dividing the primary parent by the extra dividers that were + * needed for the alt. + */ + pr_debug("%s: setting div %lu as alt-rate %lu > old-rate %lu\n", + __func__, alt_div, alt_prate, ndata->old_rate); + + writel(HIWORD_UPDATE(alt_div, reg_data->div_core_mask, + reg_data->div_core_shift) | + HIWORD_UPDATE(1, 1, reg_data->mux_core_shift), + cpuclk->reg_base + reg_data->core_reg); + } else { + /* select alternate parent */ + writel(HIWORD_UPDATE(1, 1, reg_data->mux_core_shift), + cpuclk->reg_base + reg_data->core_reg); + } + + spin_unlock(cpuclk->lock); + return 0; +} + +static int rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk *cpuclk, + struct clk_notifier_data *ndata) +{ + const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data; + const struct rockchip_cpuclk_rate_table *rate; + + rate = rockchip_get_cpuclk_settings(cpuclk, ndata->new_rate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for cpuclk\n", + __func__, ndata->new_rate); + return -EINVAL; + } + + spin_lock(cpuclk->lock); + + if (ndata->old_rate < ndata->new_rate) + rockchip_cpuclk_set_dividers(cpuclk, rate); + + /* + * post-rate change event, re-mux to primary parent and remove dividers. + * + * NOTE: we do this in a single transaction so we're never dividing the + * primary parent by the extra dividers that were needed for the alt. + */ + + writel(HIWORD_UPDATE(0, reg_data->div_core_mask, + reg_data->div_core_shift) | + HIWORD_UPDATE(0, 1, reg_data->mux_core_shift), + cpuclk->reg_base + reg_data->core_reg); + + if (ndata->old_rate > ndata->new_rate) + rockchip_cpuclk_set_dividers(cpuclk, rate); + + spin_unlock(cpuclk->lock); + return 0; +} + +/* + * This clock notifier is called when the frequency of the parent clock + * of cpuclk is to be changed. This notifier handles the setting up all + * the divider clocks, remux to temporary parent and handling the safe + * frequency levels when using temporary parent. + */ +static int rockchip_cpuclk_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct clk_notifier_data *ndata = data; + struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_nb(nb); + int ret = 0; + + pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n", + __func__, event, ndata->old_rate, ndata->new_rate); + if (event == PRE_RATE_CHANGE) + ret = rockchip_cpuclk_pre_rate_change(cpuclk, ndata); + else if (event == POST_RATE_CHANGE) + ret = rockchip_cpuclk_post_rate_change(cpuclk, ndata); + + return notifier_from_errno(ret); +} + +struct clk *rockchip_clk_register_cpuclk(const char *name, + const char **parent_names, u8 num_parents, + const struct rockchip_cpuclk_reg_data *reg_data, + const struct rockchip_cpuclk_rate_table *rates, + int nrates, void __iomem *reg_base, spinlock_t *lock) +{ + struct rockchip_cpuclk *cpuclk; + struct clk_init_data init; + struct clk *clk, *cclk; + int ret; + + if (num_parents != 2) { + pr_err("%s: needs two parent clocks\n", __func__); + return ERR_PTR(-EINVAL); + } + + cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL); + if (!cpuclk) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.parent_names = &parent_names[0]; + init.num_parents = 1; + init.ops = &rockchip_cpuclk_ops; + + /* only allow rate changes when we have a rate table */ + init.flags = (nrates > 0) ? CLK_SET_RATE_PARENT : 0; + + /* disallow automatic parent changes by ccf */ + init.flags |= CLK_SET_RATE_NO_REPARENT; + + init.flags |= CLK_GET_RATE_NOCACHE; + + cpuclk->reg_base = reg_base; + cpuclk->lock = lock; + cpuclk->reg_data = reg_data; + cpuclk->clk_nb.notifier_call = rockchip_cpuclk_notifier_cb; + cpuclk->hw.init = &init; + + cpuclk->alt_parent = __clk_lookup(parent_names[1]); + if (!cpuclk->alt_parent) { + pr_err("%s: could not lookup alternate parent\n", + __func__); + ret = -EINVAL; + goto free_cpuclk; + } + + ret = clk_prepare_enable(cpuclk->alt_parent); + if (ret) { + pr_err("%s: could not enable alternate parent\n", + __func__); + goto free_cpuclk; + } + + clk = __clk_lookup(parent_names[0]); + if (!clk) { + pr_err("%s: could not lookup parent clock %s\n", + __func__, parent_names[0]); + ret = -EINVAL; + goto free_cpuclk; + } + + ret = clk_notifier_register(clk, &cpuclk->clk_nb); + if (ret) { + pr_err("%s: failed to register clock notifier for %s\n", + __func__, name); + goto free_cpuclk; + } + + if (nrates > 0) { + cpuclk->rate_count = nrates; + cpuclk->rate_table = kmemdup(rates, + sizeof(*rates) * nrates, + GFP_KERNEL); + if (!cpuclk->rate_table) { + pr_err("%s: could not allocate memory for cpuclk rates\n", + __func__); + ret = -ENOMEM; + goto unregister_notifier; + } + } + + cclk = clk_register(NULL, &cpuclk->hw); + if (IS_ERR(clk)) { + pr_err("%s: could not register cpuclk %s\n", __func__, name); + ret = PTR_ERR(clk); + goto free_rate_table; + } + + return cclk; + +free_rate_table: + kfree(cpuclk->rate_table); +unregister_notifier: + clk_notifier_unregister(clk, &cpuclk->clk_nb); +free_cpuclk: + kfree(cpuclk); + return ERR_PTR(ret); +} diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index f2a1c7abf4d952c008c7e65c45e92fb53f47ff3e..a3e886a38480a75060d310bc128944e54169c80d 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -34,7 +34,6 @@ struct rockchip_clk_pll { const struct clk_ops *pll_mux_ops; struct notifier_block clk_nb; - bool rate_change_remuxed; void __iomem *reg_base; int lock_offset; @@ -108,38 +107,6 @@ static int rockchip_pll_wait_lock(struct rockchip_clk_pll *pll) return -ETIMEDOUT; } -/** - * Set pll mux when changing the pll rate. - * This makes sure to move the pll mux away from the actual pll before - * changing its rate and back to the original parent after the change. - */ -static int rockchip_pll_notifier_cb(struct notifier_block *nb, - unsigned long event, void *data) -{ - struct rockchip_clk_pll *pll = to_rockchip_clk_pll_nb(nb); - struct clk_mux *pll_mux = &pll->pll_mux; - const struct clk_ops *pll_mux_ops = pll->pll_mux_ops; - int cur_parent; - - switch (event) { - case PRE_RATE_CHANGE: - cur_parent = pll_mux_ops->get_parent(&pll_mux->hw); - if (cur_parent == PLL_MODE_NORM) { - pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW); - pll->rate_change_remuxed = 1; - } - break; - case POST_RATE_CHANGE: - if (pll->rate_change_remuxed) { - pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM); - pll->rate_change_remuxed = 0; - } - break; - } - - return NOTIFY_OK; -} - /** * PLL used in RK3066, RK3188 and RK3288 */ @@ -194,6 +161,10 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, const struct rockchip_pll_rate_table *rate; unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate); struct regmap *grf = rockchip_clk_get_grf(); + struct clk_mux *pll_mux = &pll->pll_mux; + const struct clk_ops *pll_mux_ops = pll->pll_mux_ops; + int rate_change_remuxed = 0; + int cur_parent; int ret; if (IS_ERR(grf)) { @@ -216,6 +187,12 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, pr_debug("%s: rate settings for %lu (nr, no, nf): (%d, %d, %d)\n", __func__, rate->rate, rate->nr, rate->no, rate->nf); + cur_parent = pll_mux_ops->get_parent(&pll_mux->hw); + if (cur_parent == PLL_MODE_NORM) { + pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW); + rate_change_remuxed = 1; + } + /* enter reset mode */ writel(HIWORD_UPDATE(RK3066_PLLCON3_RESET, RK3066_PLLCON3_RESET, 0), pll->reg_base + RK3066_PLLCON(3)); @@ -247,6 +224,9 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate, rockchip_rk3066_pll_set_rate(hw, old_rate, prate); } + if (rate_change_remuxed) + pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM); + return ret; } @@ -310,7 +290,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, struct clk_mux *pll_mux; struct clk *pll_clk, *mux_clk; char pll_name[20]; - int ret; if (num_parents != 2) { pr_err("%s: needs two parent clocks\n", __func__); @@ -367,7 +346,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, pll->lock_offset = grf_lock_offset; pll->lock_shift = lock_shift; pll->lock = lock; - pll->clk_nb.notifier_call = rockchip_pll_notifier_cb; pll_clk = clk_register(NULL, &pll->hw); if (IS_ERR(pll_clk)) { @@ -377,14 +355,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, goto err_pll; } - ret = clk_notifier_register(pll_clk, &pll->clk_nb); - if (ret) { - pr_err("%s: failed to register clock notifier for %s : %d\n", - __func__, name, ret); - mux_clk = ERR_PTR(ret); - goto err_pll_notifier; - } - /* create the mux on top of the real pll */ pll->pll_mux_ops = &clk_mux_ops; pll_mux = &pll->pll_mux; @@ -417,13 +387,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, return mux_clk; err_mux: - ret = clk_notifier_unregister(pll_clk, &pll->clk_nb); - if (ret) { - pr_err("%s: could not unregister clock notifier in error path : %d\n", - __func__, ret); - return mux_clk; - } -err_pll_notifier: clk_unregister(pll_clk); err_pll: kfree(pll); diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index a83a6d8d0fb6479b4592c672e059fe6e813bac11..beed49c79126bb1412cf7b217586aa8c0b6e532d 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -19,6 +19,7 @@ #include #include "clk.h" +#define RK3066_GRF_SOC_STATUS 0x15c #define RK3188_GRF_SOC_STATUS 0xac enum rk3188_plls { @@ -100,6 +101,98 @@ struct rockchip_pll_rate_table rk3188_pll_rates[] = { { /* sentinel */ }, }; +#define RK3066_DIV_CORE_PERIPH_MASK 0x3 +#define RK3066_DIV_CORE_PERIPH_SHIFT 6 +#define RK3066_DIV_ACLK_CORE_MASK 0x7 +#define RK3066_DIV_ACLK_CORE_SHIFT 0 +#define RK3066_DIV_ACLK_HCLK_MASK 0x3 +#define RK3066_DIV_ACLK_HCLK_SHIFT 8 +#define RK3066_DIV_ACLK_PCLK_MASK 0x3 +#define RK3066_DIV_ACLK_PCLK_SHIFT 12 +#define RK3066_DIV_AHB2APB_MASK 0x3 +#define RK3066_DIV_AHB2APB_SHIFT 14 + +#define RK3066_CLKSEL0(_core_peri) \ + { \ + .reg = RK2928_CLKSEL_CON(0), \ + .val = HIWORD_UPDATE(_core_peri, RK3066_DIV_CORE_PERIPH_MASK, \ + RK3066_DIV_CORE_PERIPH_SHIFT) \ + } +#define RK3066_CLKSEL1(_aclk_core, _aclk_hclk, _aclk_pclk, _ahb2apb) \ + { \ + .reg = RK2928_CLKSEL_CON(1), \ + .val = HIWORD_UPDATE(_aclk_core, RK3066_DIV_ACLK_CORE_MASK, \ + RK3066_DIV_ACLK_CORE_SHIFT) | \ + HIWORD_UPDATE(_aclk_hclk, RK3066_DIV_ACLK_HCLK_MASK, \ + RK3066_DIV_ACLK_HCLK_SHIFT) | \ + HIWORD_UPDATE(_aclk_pclk, RK3066_DIV_ACLK_PCLK_MASK, \ + RK3066_DIV_ACLK_PCLK_SHIFT) | \ + HIWORD_UPDATE(_ahb2apb, RK3066_DIV_AHB2APB_MASK, \ + RK3066_DIV_AHB2APB_SHIFT), \ + } + +#define RK3066_CPUCLK_RATE(_prate, _core_peri, _acore, _ahclk, _apclk, _h2p) \ + { \ + .prate = _prate, \ + .divs = { \ + RK3066_CLKSEL0(_core_peri), \ + RK3066_CLKSEL1(_acore, _ahclk, _apclk, _h2p), \ + }, \ + } + +static struct rockchip_cpuclk_rate_table rk3066_cpuclk_rates[] __initdata = { + RK3066_CPUCLK_RATE(1416000000, 2, 3, 1, 2, 1), + RK3066_CPUCLK_RATE(1200000000, 2, 3, 1, 2, 1), + RK3066_CPUCLK_RATE(1008000000, 2, 2, 1, 2, 1), + RK3066_CPUCLK_RATE( 816000000, 2, 2, 1, 2, 1), + RK3066_CPUCLK_RATE( 600000000, 1, 2, 1, 2, 1), + RK3066_CPUCLK_RATE( 504000000, 1, 1, 1, 2, 1), + RK3066_CPUCLK_RATE( 312000000, 0, 1, 1, 1, 0), +}; + +static const struct rockchip_cpuclk_reg_data rk3066_cpuclk_data = { + .core_reg = RK2928_CLKSEL_CON(0), + .div_core_shift = 0, + .div_core_mask = 0x1f, + .mux_core_shift = 8, +}; + +#define RK3188_DIV_ACLK_CORE_MASK 0x7 +#define RK3188_DIV_ACLK_CORE_SHIFT 3 + +#define RK3188_CLKSEL1(_aclk_core) \ + { \ + .reg = RK2928_CLKSEL_CON(1), \ + .val = HIWORD_UPDATE(_aclk_core, RK3188_DIV_ACLK_CORE_MASK,\ + RK3188_DIV_ACLK_CORE_SHIFT) \ + } +#define RK3188_CPUCLK_RATE(_prate, _core_peri, _aclk_core) \ + { \ + .prate = _prate, \ + .divs = { \ + RK3066_CLKSEL0(_core_peri), \ + RK3188_CLKSEL1(_aclk_core), \ + }, \ + } + +static struct rockchip_cpuclk_rate_table rk3188_cpuclk_rates[] __initdata = { + RK3188_CPUCLK_RATE(1608000000, 2, 3), + RK3188_CPUCLK_RATE(1416000000, 2, 3), + RK3188_CPUCLK_RATE(1200000000, 2, 3), + RK3188_CPUCLK_RATE(1008000000, 2, 3), + RK3188_CPUCLK_RATE( 816000000, 2, 3), + RK3188_CPUCLK_RATE( 600000000, 1, 3), + RK3188_CPUCLK_RATE( 504000000, 1, 3), + RK3188_CPUCLK_RATE( 312000000, 0, 1), +}; + +static const struct rockchip_cpuclk_reg_data rk3188_cpuclk_data = { + .core_reg = RK2928_CLKSEL_CON(0), + .div_core_shift = 9, + .div_core_mask = 0x1f, + .mux_core_shift = 8, +}; + PNAME(mux_pll_p) = { "xin24m", "xin32k" }; PNAME(mux_armclk_p) = { "apll", "gpll_armclk" }; PNAME(mux_ddrphy_p) = { "dpll", "gpll_ddr" }; @@ -173,17 +266,10 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = { GATE(0, "aclk_cpu", "aclk_cpu_pre", 0, RK2928_CLKGATE_CON(0), 3, GFLAGS), - DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0, - RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO), GATE(0, "atclk_cpu", "pclk_cpu_pre", 0, RK2928_CLKGATE_CON(0), 6, GFLAGS), GATE(0, "pclk_cpu", "pclk_cpu_pre", 0, RK2928_CLKGATE_CON(0), 5, GFLAGS), - DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0, - RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO), - COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0, - RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, - RK2928_CLKGATE_CON(4), 9, GFLAGS), GATE(0, "hclk_cpu", "hclk_cpu_pre", 0, RK2928_CLKGATE_CON(0), 4, GFLAGS), @@ -412,10 +498,18 @@ static struct clk_div_table div_aclk_cpu_t[] = { }; static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = { - COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0, - RK2928_CLKSEL_CON(0), 8, 1, MFLAGS, 0, 5, DFLAGS), DIVTBL(0, "aclk_cpu_pre", "armclk", 0, - RK2928_CLKSEL_CON(1), 0, 3, DFLAGS, div_aclk_cpu_t), + RK2928_CLKSEL_CON(1), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, div_aclk_cpu_t), + DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0, + RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO + | CLK_DIVIDER_READ_ONLY), + DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0, + RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO + | CLK_DIVIDER_READ_ONLY), + COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0, + RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO + | CLK_DIVIDER_READ_ONLY, + RK2928_CLKGATE_CON(4), 9, GFLAGS), GATE(CORE_L2C, "core_l2c", "aclk_cpu", 0, RK2928_CLKGATE_CON(9), 4, GFLAGS), @@ -524,8 +618,6 @@ PNAME(mux_hsicphy_p) = { "sclk_otgphy0", "sclk_otgphy1", "gpll", "cpll" }; static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = { - COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0, - RK2928_CLKSEL_CON(0), 8, 1, MFLAGS, 9, 5, DFLAGS), COMPOSITE_NOMUX_DIVTBL(0, "aclk_core", "armclk", 0, RK2928_CLKSEL_CON(1), 3, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, div_rk3188_aclk_core_t, RK2928_CLKGATE_CON(0), 7, GFLAGS), @@ -533,6 +625,13 @@ static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = { /* do not source aclk_cpu_pre from the apll, to keep complexity down */ COMPOSITE_NOGATE(0, "aclk_cpu_pre", mux_aclk_cpu_p, CLK_SET_RATE_NO_REPARENT, RK2928_CLKSEL_CON(0), 5, 1, MFLAGS, 0, 5, DFLAGS), + DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0, + RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO), + DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0, + RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO), + COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0, + RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, + RK2928_CLKGATE_CON(4), 9, GFLAGS), GATE(CORE_L2C, "core_l2c", "armclk", 0, RK2928_CLKGATE_CON(9), 4, GFLAGS), @@ -599,6 +698,12 @@ static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = { GATE(ACLK_GPS, "aclk_gps", "aclk_peri", 0, RK2928_CLKGATE_CON(8), 13, GFLAGS), }; +static const char *rk3188_critical_clocks[] __initconst = { + "aclk_cpu", + "aclk_peri", + "hclk_peri", +}; + static void __init rk3188_common_clk_init(struct device_node *np) { void __iomem *reg_base; @@ -623,29 +728,65 @@ static void __init rk3188_common_clk_init(struct device_node *np) pr_warn("%s: could not register clock usb480m: %ld\n", __func__, PTR_ERR(clk)); - rockchip_clk_register_plls(rk3188_pll_clks, - ARRAY_SIZE(rk3188_pll_clks), - RK3188_GRF_SOC_STATUS); rockchip_clk_register_branches(common_clk_branches, ARRAY_SIZE(common_clk_branches)); + rockchip_clk_protect_critical(rk3188_critical_clocks, + ARRAY_SIZE(rk3188_critical_clocks)); rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); + + rockchip_register_restart_notifier(RK2928_GLB_SRST_FST); } static void __init rk3066a_clk_init(struct device_node *np) { rk3188_common_clk_init(np); + rockchip_clk_register_plls(rk3188_pll_clks, + ARRAY_SIZE(rk3188_pll_clks), + RK3066_GRF_SOC_STATUS); rockchip_clk_register_branches(rk3066a_clk_branches, ARRAY_SIZE(rk3066a_clk_branches)); + rockchip_clk_register_armclk(ARMCLK, "armclk", + mux_armclk_p, ARRAY_SIZE(mux_armclk_p), + &rk3066_cpuclk_data, rk3066_cpuclk_rates, + ARRAY_SIZE(rk3066_cpuclk_rates)); } CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init); static void __init rk3188a_clk_init(struct device_node *np) { + struct clk *clk1, *clk2; + unsigned long rate; + int ret; + rk3188_common_clk_init(np); + rockchip_clk_register_plls(rk3188_pll_clks, + ARRAY_SIZE(rk3188_pll_clks), + RK3188_GRF_SOC_STATUS); rockchip_clk_register_branches(rk3188_clk_branches, ARRAY_SIZE(rk3188_clk_branches)); + rockchip_clk_register_armclk(ARMCLK, "armclk", + mux_armclk_p, ARRAY_SIZE(mux_armclk_p), + &rk3188_cpuclk_data, rk3188_cpuclk_rates, + ARRAY_SIZE(rk3188_cpuclk_rates)); + + /* reparent aclk_cpu_pre from apll */ + clk1 = __clk_lookup("aclk_cpu_pre"); + clk2 = __clk_lookup("gpll"); + if (clk1 && clk2) { + rate = clk_get_rate(clk1); + + ret = clk_set_parent(clk1, clk2); + if (ret < 0) + pr_warn("%s: could not reparent aclk_cpu_pre to gpll\n", + __func__); + + clk_set_rate(clk1, rate); + } else { + pr_warn("%s: missing clocks to reparent aclk_cpu_pre to gpll\n", + __func__); + } } CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init); diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index b22a2d2f21e978366313513b6cbb6f6fd80e8a6e..23278291da448de806a9a8929fe300c6952aafec 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c @@ -20,7 +20,7 @@ #include "clk.h" #define RK3288_GRF_SOC_CON(x) (0x244 + x * 4) -#define RK3288_GRF_SOC_STATUS 0x280 +#define RK3288_GRF_SOC_STATUS1 0x284 enum rk3288_plls { apll, dpll, cpll, gpll, npll, @@ -101,6 +101,70 @@ struct rockchip_pll_rate_table rk3288_pll_rates[] = { { /* sentinel */ }, }; +#define RK3288_DIV_ACLK_CORE_M0_MASK 0xf +#define RK3288_DIV_ACLK_CORE_M0_SHIFT 0 +#define RK3288_DIV_ACLK_CORE_MP_MASK 0xf +#define RK3288_DIV_ACLK_CORE_MP_SHIFT 4 +#define RK3288_DIV_L2RAM_MASK 0x7 +#define RK3288_DIV_L2RAM_SHIFT 0 +#define RK3288_DIV_ATCLK_MASK 0x1f +#define RK3288_DIV_ATCLK_SHIFT 4 +#define RK3288_DIV_PCLK_DBGPRE_MASK 0x1f +#define RK3288_DIV_PCLK_DBGPRE_SHIFT 9 + +#define RK3288_CLKSEL0(_core_m0, _core_mp) \ + { \ + .reg = RK3288_CLKSEL_CON(0), \ + .val = HIWORD_UPDATE(_core_m0, RK3288_DIV_ACLK_CORE_M0_MASK, \ + RK3288_DIV_ACLK_CORE_M0_SHIFT) | \ + HIWORD_UPDATE(_core_mp, RK3288_DIV_ACLK_CORE_MP_MASK, \ + RK3288_DIV_ACLK_CORE_MP_SHIFT), \ + } +#define RK3288_CLKSEL37(_l2ram, _atclk, _pclk_dbg_pre) \ + { \ + .reg = RK3288_CLKSEL_CON(37), \ + .val = HIWORD_UPDATE(_l2ram, RK3288_DIV_L2RAM_MASK, \ + RK3288_DIV_L2RAM_SHIFT) | \ + HIWORD_UPDATE(_atclk, RK3288_DIV_ATCLK_MASK, \ + RK3288_DIV_ATCLK_SHIFT) | \ + HIWORD_UPDATE(_pclk_dbg_pre, \ + RK3288_DIV_PCLK_DBGPRE_MASK, \ + RK3288_DIV_PCLK_DBGPRE_SHIFT), \ + } + +#define RK3288_CPUCLK_RATE(_prate, _core_m0, _core_mp, _l2ram, _atclk, _pdbg) \ + { \ + .prate = _prate, \ + .divs = { \ + RK3288_CLKSEL0(_core_m0, _core_mp), \ + RK3288_CLKSEL37(_l2ram, _atclk, _pdbg), \ + }, \ + } + +static struct rockchip_cpuclk_rate_table rk3288_cpuclk_rates[] __initdata = { + RK3288_CPUCLK_RATE(1800000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE(1704000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE(1608000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE(1512000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE(1416000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE(1200000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE(1008000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE( 816000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE( 696000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE( 600000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE( 408000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE( 312000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE( 216000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE( 126000000, 2, 4, 2, 4, 4), +}; + +static const struct rockchip_cpuclk_reg_data rk3288_cpuclk_data = { + .core_reg = RK3288_CLKSEL_CON(0), + .div_core_shift = 8, + .div_core_mask = 0x1f, + .mux_core_shift = 15, +}; + PNAME(mux_pll_p) = { "xin24m", "xin32k" }; PNAME(mux_armclk_p) = { "apll_core", "gpll_core" }; PNAME(mux_ddrphy_p) = { "dpll_ddr", "gpll_ddr" }; @@ -143,7 +207,7 @@ static struct rockchip_pll_clock rk3288_pll_clks[] __initdata = { [gpll] = PLL(pll_rk3066, PLL_GPLL, "gpll", mux_pll_p, 0, RK3288_PLL_CON(12), RK3288_MODE_CON, 12, 8, rk3288_pll_rates), [npll] = PLL(pll_rk3066, PLL_NPLL, "npll", mux_pll_p, 0, RK3288_PLL_CON(16), - RK3288_MODE_CON, 14, 9, NULL), + RK3288_MODE_CON, 14, 9, rk3288_pll_rates), }; static struct clk_div_table div_hclk_cpu_t[] = { @@ -166,35 +230,33 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { RK3288_CLKGATE_CON(0), 1, GFLAGS), GATE(0, "gpll_core", "gpll", 0, RK3288_CLKGATE_CON(0), 2, GFLAGS), - COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0, - RK3288_CLKSEL_CON(0), 15, 1, MFLAGS, 8, 5, DFLAGS), COMPOSITE_NOMUX(0, "armcore0", "armclk", 0, - RK3288_CLKSEL_CON(36), 0, 3, DFLAGS, + RK3288_CLKSEL_CON(36), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 0, GFLAGS), COMPOSITE_NOMUX(0, "armcore1", "armclk", 0, - RK3288_CLKSEL_CON(36), 4, 3, DFLAGS, + RK3288_CLKSEL_CON(36), 4, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 1, GFLAGS), COMPOSITE_NOMUX(0, "armcore2", "armclk", 0, - RK3288_CLKSEL_CON(36), 8, 3, DFLAGS, + RK3288_CLKSEL_CON(36), 8, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 2, GFLAGS), COMPOSITE_NOMUX(0, "armcore3", "armclk", 0, - RK3288_CLKSEL_CON(36), 12, 3, DFLAGS, + RK3288_CLKSEL_CON(36), 12, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 3, GFLAGS), COMPOSITE_NOMUX(0, "l2ram", "armclk", 0, - RK3288_CLKSEL_CON(37), 0, 3, DFLAGS, + RK3288_CLKSEL_CON(37), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 4, GFLAGS), COMPOSITE_NOMUX(0, "aclk_core_m0", "armclk", 0, - RK3288_CLKSEL_CON(0), 0, 4, DFLAGS, + RK3288_CLKSEL_CON(0), 0, 4, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 5, GFLAGS), COMPOSITE_NOMUX(0, "aclk_core_mp", "armclk", 0, - RK3288_CLKSEL_CON(0), 4, 4, DFLAGS, + RK3288_CLKSEL_CON(0), 4, 4, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 6, GFLAGS), COMPOSITE_NOMUX(0, "atclk", "armclk", 0, - RK3288_CLKSEL_CON(37), 4, 5, DFLAGS, + RK3288_CLKSEL_CON(37), 4, 5, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 7, GFLAGS), COMPOSITE_NOMUX(0, "pclk_dbg_pre", "armclk", 0, - RK3288_CLKSEL_CON(37), 9, 5, DFLAGS, + RK3288_CLKSEL_CON(37), 9, 5, DFLAGS | CLK_DIVIDER_READ_ONLY, RK3288_CLKGATE_CON(12), 8, GFLAGS), GATE(0, "pclk_dbg", "pclk_dbg_pre", 0, RK3288_CLKGATE_CON(12), 9, GFLAGS), @@ -219,12 +281,12 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { RK3288_CLKSEL_CON(1), 15, 1, MFLAGS, 3, 5, DFLAGS), DIV(0, "aclk_cpu_pre", "aclk_cpu_src", 0, RK3288_CLKSEL_CON(1), 0, 3, DFLAGS), - GATE(0, "aclk_cpu", "aclk_cpu_pre", 0, + GATE(ACLK_CPU, "aclk_cpu", "aclk_cpu_pre", 0, RK3288_CLKGATE_CON(0), 3, GFLAGS), - COMPOSITE_NOMUX(0, "pclk_cpu", "aclk_cpu_pre", 0, + COMPOSITE_NOMUX(PCLK_CPU, "pclk_cpu", "aclk_cpu_pre", 0, RK3288_CLKSEL_CON(1), 12, 3, DFLAGS, RK3288_CLKGATE_CON(0), 5, GFLAGS), - COMPOSITE_NOMUX_DIVTBL(0, "hclk_cpu", "aclk_cpu_pre", 0, + COMPOSITE_NOMUX_DIVTBL(HCLK_CPU, "hclk_cpu", "aclk_cpu_pre", 0, RK3288_CLKSEL_CON(1), 8, 2, DFLAGS, div_hclk_cpu_t, RK3288_CLKGATE_CON(0), 4, GFLAGS), GATE(0, "c2c_host", "aclk_cpu_src", 0, @@ -238,15 +300,15 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { COMPOSITE(0, "i2s_src", mux_pll_src_cpll_gpll_p, 0, RK3288_CLKSEL_CON(4), 15, 1, MFLAGS, 0, 7, DFLAGS, RK3288_CLKGATE_CON(4), 1, GFLAGS), - COMPOSITE_FRAC(0, "i2s_frac", "i2s_src", 0, + COMPOSITE_FRAC(0, "i2s_frac", "i2s_src", CLK_SET_RATE_PARENT, RK3288_CLKSEL_CON(8), 0, RK3288_CLKGATE_CON(4), 2, GFLAGS), - MUX(0, "i2s_pre", mux_i2s_pre_p, 0, + MUX(0, "i2s_pre", mux_i2s_pre_p, CLK_SET_RATE_PARENT, RK3288_CLKSEL_CON(4), 8, 2, MFLAGS), - COMPOSITE_NODIV(0, "i2s0_clkout", mux_i2s_clkout_p, 0, + COMPOSITE_NODIV(0, "i2s0_clkout", mux_i2s_clkout_p, CLK_SET_RATE_PARENT, RK3288_CLKSEL_CON(4), 12, 1, MFLAGS, RK3288_CLKGATE_CON(4), 0, GFLAGS), - GATE(SCLK_I2S0, "sclk_i2s0", "i2s_pre", 0, + GATE(SCLK_I2S0, "sclk_i2s0", "i2s_pre", CLK_SET_RATE_PARENT, RK3288_CLKGATE_CON(4), 3, GFLAGS), MUX(0, "spdif_src", mux_pll_src_cpll_gpll_p, 0, @@ -296,6 +358,20 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { COMPOSITE(0, "aclk_vdpu", mux_pll_src_cpll_gpll_usb480m_p, 0, RK3288_CLKSEL_CON(32), 14, 2, MFLAGS, 8, 5, DFLAGS, RK3288_CLKGATE_CON(3), 11, GFLAGS), + /* + * We use aclk_vdpu by default GRF_SOC_CON0[7] setting in system, + * so we ignore the mux and make clocks nodes as following, + */ + GATE(ACLK_VCODEC, "aclk_vcodec", "aclk_vdpu", 0, + RK3288_CLKGATE_CON(9), 0, GFLAGS), + /* + * We introduce a virtul node of hclk_vodec_pre_v to split one clock + * struct with a gate and a fix divider into two node in software. + */ + GATE(0, "hclk_vcodec_pre_v", "aclk_vdpu", 0, + RK3288_CLKGATE_CON(3), 10, GFLAGS), + GATE(HCLK_VCODEC, "hclk_vcodec", "hclk_vcodec_pre", 0, + RK3288_CLKGATE_CON(9), 1, GFLAGS), COMPOSITE(0, "aclk_vio0", mux_pll_src_cpll_gpll_usb480m_p, 0, RK3288_CLKSEL_CON(31), 6, 2, MFLAGS, 0, 5, DFLAGS, @@ -309,7 +385,7 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { COMPOSITE(0, "aclk_rga_pre", mux_pll_src_cpll_gpll_usb480m_p, 0, RK3288_CLKSEL_CON(30), 6, 2, MFLAGS, 0, 5, DFLAGS, RK3288_CLKGATE_CON(3), 5, GFLAGS), - COMPOSITE(0, "sclk_rga", mux_pll_src_cpll_gpll_usb480m_p, 0, + COMPOSITE(SCLK_RGA, "sclk_rga", mux_pll_src_cpll_gpll_usb480m_p, 0, RK3288_CLKSEL_CON(30), 14, 2, MFLAGS, 8, 5, DFLAGS, RK3288_CLKGATE_CON(3), 4, GFLAGS), @@ -320,35 +396,35 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { RK3288_CLKSEL_CON(29), 6, 2, MFLAGS, 8, 8, DFLAGS, RK3288_CLKGATE_CON(3), 3, GFLAGS), - COMPOSITE_NODIV(0, "sclk_edp_24m", mux_edp_24m_p, 0, + COMPOSITE_NODIV(SCLK_EDP_24M, "sclk_edp_24m", mux_edp_24m_p, 0, RK3288_CLKSEL_CON(28), 15, 1, MFLAGS, RK3288_CLKGATE_CON(3), 12, GFLAGS), - COMPOSITE(0, "sclk_edp", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(SCLK_EDP, "sclk_edp", mux_pll_src_cpll_gpll_npll_p, 0, RK3288_CLKSEL_CON(28), 6, 2, MFLAGS, 0, 6, DFLAGS, RK3288_CLKGATE_CON(3), 13, GFLAGS), - COMPOSITE(0, "sclk_isp", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(SCLK_ISP, "sclk_isp", mux_pll_src_cpll_gpll_npll_p, 0, RK3288_CLKSEL_CON(6), 6, 2, MFLAGS, 0, 6, DFLAGS, RK3288_CLKGATE_CON(3), 14, GFLAGS), - COMPOSITE(0, "sclk_isp_jpe", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(SCLK_ISP_JPE, "sclk_isp_jpe", mux_pll_src_cpll_gpll_npll_p, 0, RK3288_CLKSEL_CON(6), 14, 2, MFLAGS, 8, 6, DFLAGS, RK3288_CLKGATE_CON(3), 15, GFLAGS), - GATE(0, "sclk_hdmi_hdcp", "xin24m", 0, + GATE(SCLK_HDMI_HDCP, "sclk_hdmi_hdcp", "xin24m", 0, RK3288_CLKGATE_CON(5), 12, GFLAGS), - GATE(0, "sclk_hdmi_cec", "xin32k", 0, + GATE(SCLK_HDMI_CEC, "sclk_hdmi_cec", "xin32k", 0, RK3288_CLKGATE_CON(5), 11, GFLAGS), - COMPOSITE(0, "aclk_hevc", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(ACLK_HEVC, "aclk_hevc", mux_pll_src_cpll_gpll_npll_p, 0, RK3288_CLKSEL_CON(39), 14, 2, MFLAGS, 8, 5, DFLAGS, RK3288_CLKGATE_CON(13), 13, GFLAGS), - DIV(0, "hclk_hevc", "aclk_hevc", 0, + DIV(HCLK_HEVC, "hclk_hevc", "aclk_hevc", 0, RK3288_CLKSEL_CON(40), 12, 2, DFLAGS), - COMPOSITE(0, "sclk_hevc_cabac", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(SCLK_HEVC_CABAC, "sclk_hevc_cabac", mux_pll_src_cpll_gpll_npll_p, 0, RK3288_CLKSEL_CON(42), 6, 2, MFLAGS, 0, 5, DFLAGS, RK3288_CLKGATE_CON(13), 14, GFLAGS), - COMPOSITE(0, "sclk_hevc_core", mux_pll_src_cpll_gpll_npll_p, 0, + COMPOSITE(SCLK_HEVC_CORE, "sclk_hevc_core", mux_pll_src_cpll_gpll_npll_p, 0, RK3288_CLKSEL_CON(42), 14, 2, MFLAGS, 8, 5, DFLAGS, RK3288_CLKGATE_CON(13), 15, GFLAGS), @@ -371,13 +447,13 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { COMPOSITE(0, "aclk_peri_src", mux_pll_src_cpll_gpll_p, 0, RK3288_CLKSEL_CON(10), 15, 1, MFLAGS, 0, 5, DFLAGS, RK3288_CLKGATE_CON(2), 0, GFLAGS), - COMPOSITE_NOMUX(0, "pclk_peri", "aclk_peri_src", 0, + COMPOSITE_NOMUX(PCLK_PERI, "pclk_peri", "aclk_peri_src", 0, RK3288_CLKSEL_CON(10), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, RK3288_CLKGATE_CON(2), 3, GFLAGS), - COMPOSITE_NOMUX(0, "hclk_peri", "aclk_peri_src", 0, + COMPOSITE_NOMUX(HCLK_PERI, "hclk_peri", "aclk_peri_src", 0, RK3288_CLKSEL_CON(10), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, RK3288_CLKGATE_CON(2), 2, GFLAGS), - GATE(0, "aclk_peri", "aclk_peri_src", 0, + GATE(ACLK_PERI, "aclk_peri", "aclk_peri_src", 0, RK3288_CLKGATE_CON(2), 1, GFLAGS), /* @@ -643,34 +719,34 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { GATE(HCLK_RGA, "hclk_rga", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 1, GFLAGS), GATE(HCLK_VOP0, "hclk_vop0", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 6, GFLAGS), GATE(HCLK_VOP1, "hclk_vop1", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 8, GFLAGS), - GATE(0, "hclk_vio_ahb_arbi", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 9, GFLAGS), - GATE(0, "hclk_vio_niu", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 10, GFLAGS), - GATE(0, "hclk_vip", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 15, GFLAGS), + GATE(HCLK_VIO_AHB_ARBI, "hclk_vio_ahb_arbi", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 9, GFLAGS), + GATE(HCLK_VIO_NIU, "hclk_vio_niu", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 10, GFLAGS), + GATE(HCLK_VIP, "hclk_vip", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 15, GFLAGS), GATE(HCLK_IEP, "hclk_iep", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 3, GFLAGS), GATE(HCLK_ISP, "hclk_isp", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 1, GFLAGS), - GATE(0, "hclk_vio2_h2p", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 10, GFLAGS), - GATE(0, "pclk_mipi_dsi0", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 4, GFLAGS), - GATE(0, "pclk_mipi_dsi1", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 5, GFLAGS), - GATE(0, "pclk_mipi_csi", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 6, GFLAGS), - GATE(0, "pclk_lvds_phy", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 7, GFLAGS), - GATE(0, "pclk_edp_ctrl", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 8, GFLAGS), - GATE(0, "pclk_hdmi_ctrl", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 9, GFLAGS), - GATE(0, "pclk_vio2_h2p", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 11, GFLAGS), + GATE(HCLK_VIO2_H2P, "hclk_vio2_h2p", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 10, GFLAGS), + GATE(PCLK_MIPI_DSI0, "pclk_mipi_dsi0", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 4, GFLAGS), + GATE(PCLK_MIPI_DSI1, "pclk_mipi_dsi1", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 5, GFLAGS), + GATE(PCLK_MIPI_CSI, "pclk_mipi_csi", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 6, GFLAGS), + GATE(PCLK_LVDS_PHY, "pclk_lvds_phy", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 7, GFLAGS), + GATE(PCLK_EDP_CTRL, "pclk_edp_ctrl", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 8, GFLAGS), + GATE(PCLK_HDMI_CTRL, "pclk_hdmi_ctrl", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 9, GFLAGS), + GATE(PCLK_VIO2_H2P, "pclk_vio2_h2p", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 11, GFLAGS), /* aclk_vio0 gates */ GATE(ACLK_VOP0, "aclk_vop0", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 5, GFLAGS), - GATE(0, "aclk_iep", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 2, GFLAGS), - GATE(0, "aclk_vio0_niu", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 11, GFLAGS), - GATE(0, "aclk_vip", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 14, GFLAGS), + GATE(ACLK_IEP, "aclk_iep", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 2, GFLAGS), + GATE(ACLK_VIO0_NIU, "aclk_vio0_niu", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 11, GFLAGS), + GATE(ACLK_VIP, "aclk_vip", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 14, GFLAGS), /* aclk_vio1 gates */ GATE(ACLK_VOP1, "aclk_vop1", "aclk_vio1", 0, RK3288_CLKGATE_CON(15), 7, GFLAGS), - GATE(0, "aclk_isp", "aclk_vio1", 0, RK3288_CLKGATE_CON(16), 2, GFLAGS), - GATE(0, "aclk_vio1_niu", "aclk_vio1", 0, RK3288_CLKGATE_CON(15), 12, GFLAGS), + GATE(ACLK_ISP, "aclk_isp", "aclk_vio1", 0, RK3288_CLKGATE_CON(16), 2, GFLAGS), + GATE(ACLK_VIO1_NIU, "aclk_vio1_niu", "aclk_vio1", 0, RK3288_CLKGATE_CON(15), 12, GFLAGS), /* aclk_rga_pre gates */ GATE(ACLK_RGA, "aclk_rga", "aclk_rga_pre", 0, RK3288_CLKGATE_CON(15), 0, GFLAGS), - GATE(0, "aclk_rga_niu", "aclk_rga_pre", 0, RK3288_CLKGATE_CON(15), 13, GFLAGS), + GATE(ACLK_RGA_NIU, "aclk_rga_niu", "aclk_rga_pre", 0, RK3288_CLKGATE_CON(15), 13, GFLAGS), /* * Other ungrouped clocks. @@ -680,6 +756,12 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { GATE(0, "pclk_isp_in", "ext_isp", 0, RK3288_CLKGATE_CON(16), 3, GFLAGS), }; +static const char *rk3288_critical_clocks[] __initconst = { + "aclk_cpu", + "aclk_peri", + "hclk_peri", +}; + static void __init rk3288_clk_init(struct device_node *np) { void __iomem *reg_base; @@ -705,13 +787,28 @@ static void __init rk3288_clk_init(struct device_node *np) pr_warn("%s: could not register clock usb480m: %ld\n", __func__, PTR_ERR(clk)); + clk = clk_register_fixed_factor(NULL, "hclk_vcodec_pre", + "hclk_vcodec_pre_v", 0, 1, 4); + if (IS_ERR(clk)) + pr_warn("%s: could not register clock hclk_vcodec_pre: %ld\n", + __func__, PTR_ERR(clk)); + rockchip_clk_register_plls(rk3288_pll_clks, ARRAY_SIZE(rk3288_pll_clks), - RK3288_GRF_SOC_STATUS); + RK3288_GRF_SOC_STATUS1); rockchip_clk_register_branches(rk3288_clk_branches, ARRAY_SIZE(rk3288_clk_branches)); + rockchip_clk_protect_critical(rk3288_critical_clocks, + ARRAY_SIZE(rk3288_critical_clocks)); - rockchip_register_softrst(np, 9, reg_base + RK3288_SOFTRST_CON(0), + rockchip_clk_register_armclk(ARMCLK, "armclk", + mux_armclk_p, ARRAY_SIZE(mux_armclk_p), + &rk3288_cpuclk_data, rk3288_cpuclk_rates, + ARRAY_SIZE(rk3288_cpuclk_rates)); + + rockchip_register_softrst(np, 12, reg_base + RK3288_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); + + rockchip_register_restart_notifier(RK3288_GLB_SRST_FST); } CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init); diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index 278cf9dd1e237ecb3bb56048f321bb2ff6970549..1e68bff481b8e32ec440959002a2467287c269da 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "clk.h" /** @@ -37,7 +38,7 @@ * * sometimes without one of those components. */ -struct clk *rockchip_clk_register_branch(const char *name, +static struct clk *rockchip_clk_register_branch(const char *name, const char **parent_names, u8 num_parents, void __iomem *base, int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags, u8 div_shift, u8 div_width, u8 div_flags, @@ -103,6 +104,54 @@ struct clk *rockchip_clk_register_branch(const char *name, return clk; } +static struct clk *rockchip_clk_register_frac_branch(const char *name, + const char **parent_names, u8 num_parents, void __iomem *base, + int muxdiv_offset, u8 div_flags, + int gate_offset, u8 gate_shift, u8 gate_flags, + unsigned long flags, spinlock_t *lock) +{ + struct clk *clk; + struct clk_gate *gate = NULL; + struct clk_fractional_divider *div = NULL; + const struct clk_ops *div_ops = NULL, *gate_ops = NULL; + + if (gate_offset >= 0) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + gate->flags = gate_flags; + gate->reg = base + gate_offset; + gate->bit_idx = gate_shift; + gate->lock = lock; + gate_ops = &clk_gate_ops; + } + + if (muxdiv_offset < 0) + return ERR_PTR(-EINVAL); + + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + div->flags = div_flags; + div->reg = base + muxdiv_offset; + div->mshift = 16; + div->mmask = 0xffff0000; + div->nshift = 0; + div->nmask = 0xffff; + div->lock = lock; + div_ops = &clk_fractional_divider_ops; + + clk = clk_register_composite(NULL, name, parent_names, num_parents, + NULL, NULL, + &div->hw, div_ops, + gate ? &gate->hw : NULL, gate_ops, + flags); + + return clk; +} + static DEFINE_SPINLOCK(clk_lock); static struct clk **clk_table; static void __iomem *reg_base; @@ -197,8 +246,14 @@ void __init rockchip_clk_register_branches( list->div_flags, &clk_lock); break; case branch_fraction_divider: - /* unimplemented */ - continue; + /* keep all gates untouched for now */ + flags |= CLK_IGNORE_UNUSED; + + clk = rockchip_clk_register_frac_branch(list->name, + list->parent_names, list->num_parents, + reg_base, list->muxdiv_offset, list->div_flags, + list->gate_offset, list->gate_shift, + list->gate_flags, flags, &clk_lock); break; case branch_gate: flags |= CLK_SET_RATE_PARENT; @@ -242,3 +297,61 @@ void __init rockchip_clk_register_branches( rockchip_clk_add_lookup(clk, list->id); } } + +void __init rockchip_clk_register_armclk(unsigned int lookup_id, + const char *name, const char **parent_names, + u8 num_parents, + const struct rockchip_cpuclk_reg_data *reg_data, + const struct rockchip_cpuclk_rate_table *rates, + int nrates) +{ + struct clk *clk; + + clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents, + reg_data, rates, nrates, reg_base, + &clk_lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s: %ld\n", + __func__, name, PTR_ERR(clk)); + return; + } + + rockchip_clk_add_lookup(clk, lookup_id); +} + +void __init rockchip_clk_protect_critical(const char *clocks[], int nclocks) +{ + int i; + + /* Protect the clocks that needs to stay on */ + for (i = 0; i < nclocks; i++) { + struct clk *clk = __clk_lookup(clocks[i]); + + if (clk) + clk_prepare_enable(clk); + } +} + +static unsigned int reg_restart; +static int rockchip_restart_notify(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + writel(0xfdb9, reg_base + reg_restart); + return NOTIFY_DONE; +} + +static struct notifier_block rockchip_restart_handler = { + .notifier_call = rockchip_restart_notify, + .priority = 128, +}; + +void __init rockchip_register_restart_notifier(unsigned int reg) +{ + int ret; + + reg_restart = reg; + ret = register_restart_handler(&rockchip_restart_handler); + if (ret) + pr_err("%s: cannot register restart handler, %d\n", + __func__, ret); +} diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index 887cbdeca2aaaa7d86ed9c7cda1000092a16fce1..ca009ab0a33a28ad507d83cf9cf0de183b33a09e 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -120,6 +120,38 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, struct rockchip_pll_rate_table *rate_table, spinlock_t *lock); +struct rockchip_cpuclk_clksel { + int reg; + u32 val; +}; + +#define ROCKCHIP_CPUCLK_NUM_DIVIDERS 2 +struct rockchip_cpuclk_rate_table { + unsigned long prate; + struct rockchip_cpuclk_clksel divs[ROCKCHIP_CPUCLK_NUM_DIVIDERS]; +}; + +/** + * struct rockchip_cpuclk_reg_data: describes register offsets and masks of the cpuclock + * @core_reg: register offset of the core settings register + * @div_core_shift: core divider offset used to divide the pll value + * @div_core_mask: core divider mask + * @mux_core_shift: offset of the core multiplexer + */ +struct rockchip_cpuclk_reg_data { + int core_reg; + u8 div_core_shift; + u32 div_core_mask; + int mux_core_reg; + u8 mux_core_shift; +}; + +struct clk *rockchip_clk_register_cpuclk(const char *name, + const char **parent_names, u8 num_parents, + const struct rockchip_cpuclk_reg_data *reg_data, + const struct rockchip_cpuclk_rate_table *rates, + int nrates, void __iomem *reg_base, spinlock_t *lock); + #define PNAME(x) static const char *x[] __initconst enum rockchip_clk_branch_type { @@ -329,6 +361,13 @@ void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list, unsigned int nr_clk); void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list, unsigned int nr_pll, int grf_lock_offset); +void rockchip_clk_register_armclk(unsigned int lookup_id, const char *name, + const char **parent_names, u8 num_parents, + const struct rockchip_cpuclk_reg_data *reg_data, + const struct rockchip_cpuclk_rate_table *rates, + int nrates); +void rockchip_clk_protect_critical(const char *clocks[], int nclocks); +void rockchip_register_restart_notifier(unsigned int reg); #define ROCKCHIP_SOFTRST_HIWORD_MASK BIT(0) diff --git a/drivers/clk/samsung/clk-exynos3250.c b/drivers/clk/samsung/clk-exynos3250.c index dc85f8e7a2d77530ae89036a23e6737291ef6d33..6e6cca3920829b5a7dc0685a071389a2d201d780 100644 --- a/drivers/clk/samsung/clk-exynos3250.c +++ b/drivers/clk/samsung/clk-exynos3250.c @@ -110,7 +110,14 @@ enum exynos3250_plls { nr_plls }; +/* list of PLLs in DMC block to be registered */ +enum exynos3250_dmc_plls { + bpll, epll, + nr_dmc_plls +}; + static void __iomem *reg_base; +static void __iomem *dmc_reg_base; /* * Support for CMU save/restore across system suspends @@ -266,6 +273,7 @@ PNAME(group_sclk_cam_blk_p) = { "xxti", "xusbxti", "none", "none", "none", "none", "div_mpll_pre", "mout_epll_user", "mout_vpll", + "none", "none", "none", "div_cam_blk_320", }; PNAME(group_sclk_fimd0_p) = { "xxti", "xusbxti", "m_bitclkhsdiv4_2l", "none", @@ -353,8 +361,8 @@ static struct samsung_mux_clock mux_clks[] __initdata = { /* SRC_FSYS */ MUX(CLK_MOUT_TSADC, "mout_tsadc", group_sclk_p, SRC_FSYS, 28, 4), - MUX(CLK_MOUT_MMC1, "mout_mmc1", group_sclk_p, SRC_FSYS, 4, 3), - MUX(CLK_MOUT_MMC0, "mout_mmc0", group_sclk_p, SRC_FSYS, 0, 3), + MUX(CLK_MOUT_MMC1, "mout_mmc1", group_sclk_p, SRC_FSYS, 4, 4), + MUX(CLK_MOUT_MMC0, "mout_mmc0", group_sclk_p, SRC_FSYS, 0, 4), /* SRC_PERIL0 */ MUX(CLK_MOUT_UART1, "mout_uart1", group_sclk_p, SRC_PERIL0, 4, 4), @@ -423,7 +431,7 @@ static struct samsung_div_clock div_clks[] __initdata = { DIV(CLK_DIV_SPI1_ISP, "div_spi1_isp", "mout_spi1_isp", DIV_ISP, 16, 4), DIV_F(CLK_DIV_SPI0_ISP_PRE, "div_spi0_isp_pre", "div_spi0_isp", DIV_ISP, 8, 8, CLK_SET_RATE_PARENT, 0), - DIV(CLK_DIV_SPI0_ISP, "div_spi0_isp", "mout_spi0_isp", DIV_ISP, 0, 4), + DIV(CLK_DIV_SPI0_ISP, "div_spi0_isp", "mout_spi0_isp", DIV_ISP, 4, 4), /* DIV_FSYS0 */ DIV_F(CLK_DIV_TSADC_PRE, "div_tsadc_pre", "div_tsadc", DIV_FSYS0, 8, 8, @@ -724,6 +732,25 @@ static struct samsung_pll_rate_table exynos3250_pll_rates[] = { { /* sentinel */ } }; +/* EPLL */ +static struct samsung_pll_rate_table exynos3250_epll_rates[] = { + PLL_36XX_RATE(800000000, 200, 3, 1, 0), + PLL_36XX_RATE(288000000, 96, 2, 2, 0), + PLL_36XX_RATE(192000000, 128, 2, 3, 0), + PLL_36XX_RATE(144000000, 96, 2, 3, 0), + PLL_36XX_RATE( 96000000, 128, 2, 4, 0), + PLL_36XX_RATE( 84000000, 112, 2, 4, 0), + PLL_36XX_RATE( 80000004, 106, 2, 4, 43691), + PLL_36XX_RATE( 73728000, 98, 2, 4, 19923), + PLL_36XX_RATE( 67737598, 270, 3, 5, 62285), + PLL_36XX_RATE( 65535999, 174, 2, 5, 49982), + PLL_36XX_RATE( 50000000, 200, 3, 5, 0), + PLL_36XX_RATE( 49152002, 131, 2, 5, 4719), + PLL_36XX_RATE( 48000000, 128, 2, 5, 0), + PLL_36XX_RATE( 45158401, 180, 3, 5, 41524), + { /* sentinel */ } +}; + /* VPLL */ static struct samsung_pll_rate_table exynos3250_vpll_rates[] = { PLL_36XX_RATE(600000000, 100, 2, 1, 0), @@ -821,3 +848,172 @@ static void __init exynos3250_cmu_init(struct device_node *np) samsung_clk_of_add_provider(np, ctx); } CLK_OF_DECLARE(exynos3250_cmu, "samsung,exynos3250-cmu", exynos3250_cmu_init); + +/* + * CMU DMC + */ + +#define BPLL_LOCK 0x0118 +#define BPLL_CON0 0x0218 +#define BPLL_CON1 0x021c +#define BPLL_CON2 0x0220 +#define SRC_DMC 0x0300 +#define DIV_DMC1 0x0504 +#define GATE_BUS_DMC0 0x0700 +#define GATE_BUS_DMC1 0x0704 +#define GATE_BUS_DMC2 0x0708 +#define GATE_BUS_DMC3 0x070c +#define GATE_SCLK_DMC 0x0800 +#define GATE_IP_DMC0 0x0900 +#define GATE_IP_DMC1 0x0904 +#define EPLL_LOCK 0x1110 +#define EPLL_CON0 0x1114 +#define EPLL_CON1 0x1118 +#define EPLL_CON2 0x111c +#define SRC_EPLL 0x1120 + +/* + * Support for CMU save/restore across system suspends + */ +#ifdef CONFIG_PM_SLEEP +static struct samsung_clk_reg_dump *exynos3250_dmc_clk_regs; + +static unsigned long exynos3250_cmu_dmc_clk_regs[] __initdata = { + BPLL_LOCK, + BPLL_CON0, + BPLL_CON1, + BPLL_CON2, + SRC_DMC, + DIV_DMC1, + GATE_BUS_DMC0, + GATE_BUS_DMC1, + GATE_BUS_DMC2, + GATE_BUS_DMC3, + GATE_SCLK_DMC, + GATE_IP_DMC0, + GATE_IP_DMC1, + EPLL_LOCK, + EPLL_CON0, + EPLL_CON1, + EPLL_CON2, + SRC_EPLL, +}; + +static int exynos3250_dmc_clk_suspend(void) +{ + samsung_clk_save(dmc_reg_base, exynos3250_dmc_clk_regs, + ARRAY_SIZE(exynos3250_cmu_dmc_clk_regs)); + return 0; +} + +static void exynos3250_dmc_clk_resume(void) +{ + samsung_clk_restore(dmc_reg_base, exynos3250_dmc_clk_regs, + ARRAY_SIZE(exynos3250_cmu_dmc_clk_regs)); +} + +static struct syscore_ops exynos3250_dmc_clk_syscore_ops = { + .suspend = exynos3250_dmc_clk_suspend, + .resume = exynos3250_dmc_clk_resume, +}; + +static void exynos3250_dmc_clk_sleep_init(void) +{ + exynos3250_dmc_clk_regs = + samsung_clk_alloc_reg_dump(exynos3250_cmu_dmc_clk_regs, + ARRAY_SIZE(exynos3250_cmu_dmc_clk_regs)); + if (!exynos3250_dmc_clk_regs) { + pr_warn("%s: Failed to allocate sleep save data\n", __func__); + goto err; + } + + register_syscore_ops(&exynos3250_dmc_clk_syscore_ops); + return; +err: + kfree(exynos3250_dmc_clk_regs); +} +#else +static inline void exynos3250_dmc_clk_sleep_init(void) { } +#endif + +PNAME(mout_epll_p) = { "fin_pll", "fout_epll", }; +PNAME(mout_bpll_p) = { "fin_pll", "fout_bpll", }; +PNAME(mout_mpll_mif_p) = { "fin_pll", "sclk_mpll_mif", }; +PNAME(mout_dphy_p) = { "mout_mpll_mif", "mout_bpll", }; + +static struct samsung_mux_clock dmc_mux_clks[] __initdata = { + /* + * NOTE: Following table is sorted by register address in ascending + * order and then bitfield shift in descending order, as it is done + * in the User's Manual. When adding new entries, please make sure + * that the order is preserved, to avoid merge conflicts and make + * further work with defined data easier. + */ + + /* SRC_DMC */ + MUX(CLK_MOUT_MPLL_MIF, "mout_mpll_mif", mout_mpll_mif_p, SRC_DMC, 12, 1), + MUX(CLK_MOUT_BPLL, "mout_bpll", mout_bpll_p, SRC_DMC, 10, 1), + MUX(CLK_MOUT_DPHY, "mout_dphy", mout_dphy_p, SRC_DMC, 8, 1), + MUX(CLK_MOUT_DMC_BUS, "mout_dmc_bus", mout_dphy_p, SRC_DMC, 4, 1), + + /* SRC_EPLL */ + MUX(CLK_MOUT_EPLL, "mout_epll", mout_epll_p, SRC_EPLL, 4, 1), +}; + +static struct samsung_div_clock dmc_div_clks[] __initdata = { + /* + * NOTE: Following table is sorted by register address in ascending + * order and then bitfield shift in descending order, as it is done + * in the User's Manual. When adding new entries, please make sure + * that the order is preserved, to avoid merge conflicts and make + * further work with defined data easier. + */ + + /* DIV_DMC1 */ + DIV(CLK_DIV_DMC, "div_dmc", "div_dmc_pre", DIV_DMC1, 27, 3), + DIV(CLK_DIV_DPHY, "div_dphy", "mout_dphy", DIV_DMC1, 23, 3), + DIV(CLK_DIV_DMC_PRE, "div_dmc_pre", "mout_dmc_bus", DIV_DMC1, 19, 2), + DIV(CLK_DIV_DMCP, "div_dmcp", "div_dmcd", DIV_DMC1, 15, 3), + DIV(CLK_DIV_DMCD, "div_dmcd", "div_dmc", DIV_DMC1, 11, 3), +}; + +static struct samsung_pll_clock exynos3250_dmc_plls[nr_dmc_plls] __initdata = { + [bpll] = PLL(pll_35xx, CLK_FOUT_BPLL, "fout_bpll", "fin_pll", + BPLL_LOCK, BPLL_CON0, NULL), + [epll] = PLL(pll_36xx, CLK_FOUT_EPLL, "fout_epll", "fin_pll", + EPLL_LOCK, EPLL_CON0, NULL), +}; + +static void __init exynos3250_cmu_dmc_init(struct device_node *np) +{ + struct samsung_clk_provider *ctx; + + dmc_reg_base = of_iomap(np, 0); + if (!dmc_reg_base) + panic("%s: failed to map registers\n", __func__); + + ctx = samsung_clk_init(np, dmc_reg_base, NR_CLKS_DMC); + if (!ctx) + panic("%s: unable to allocate context.\n", __func__); + + exynos3250_dmc_plls[bpll].rate_table = exynos3250_pll_rates; + exynos3250_dmc_plls[epll].rate_table = exynos3250_epll_rates; + + pr_err("CLK registering epll bpll: %d, %d, %d, %d\n", + exynos3250_dmc_plls[bpll].rate_table[0].rate, + exynos3250_dmc_plls[bpll].rate_table[0].mdiv, + exynos3250_dmc_plls[bpll].rate_table[0].pdiv, + exynos3250_dmc_plls[bpll].rate_table[0].sdiv + ); + samsung_clk_register_pll(ctx, exynos3250_dmc_plls, + ARRAY_SIZE(exynos3250_dmc_plls), dmc_reg_base); + + samsung_clk_register_mux(ctx, dmc_mux_clks, ARRAY_SIZE(dmc_mux_clks)); + samsung_clk_register_div(ctx, dmc_div_clks, ARRAY_SIZE(dmc_div_clks)); + + exynos3250_dmc_clk_sleep_init(); + + samsung_clk_of_add_provider(np, ctx); +} +CLK_OF_DECLARE(exynos3250_cmu_dmc, "samsung,exynos3250-cmu-dmc", + exynos3250_cmu_dmc_init); diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index ac163d7f5bc35e89f5b37c7525833dfdcdbf72a6..940f02837b824869932d3e6b63462942577a708c 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -517,7 +517,7 @@ static struct samsung_fixed_factor_clock exynos4_fixed_factor_clks[] __initdata FFACTOR(0, "sclk_apll_div_2", "sclk_apll", 1, 2, 0), FFACTOR(0, "fout_mpll_div_2", "fout_mpll", 1, 2, 0), FFACTOR(0, "fout_apll_div_2", "fout_apll", 1, 2, 0), - FFACTOR(0, "arm_clk_div_2", "arm_clk", 1, 2, 0), + FFACTOR(0, "arm_clk_div_2", "div_core2", 1, 2, 0), }; static struct samsung_fixed_factor_clock exynos4210_fixed_factor_clks[] __initdata = { @@ -535,7 +535,7 @@ static struct samsung_fixed_factor_clock exynos4x12_fixed_factor_clks[] __initda static struct samsung_mux_clock exynos4_mux_clks[] __initdata = { MUX_FA(CLK_MOUT_APLL, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, CLK_SET_RATE_PARENT, 0, "mout_apll"), - MUX(0, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1), + MUX(CLK_MOUT_HDMI, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1), MUX(0, "mout_mfc1", sclk_evpll_p, SRC_MFC, 4, 1), MUX(0, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1), MUX_F(CLK_MOUT_G3D1, "mout_g3d1", sclk_evpll_p, SRC_G3D, 4, 1, @@ -569,7 +569,7 @@ static struct samsung_mux_clock exynos4210_mux_clks[] __initdata = { MUX(0, "mout_aclk100", sclk_ampll_p4210, SRC_TOP0, 16, 1), MUX(0, "mout_aclk160", sclk_ampll_p4210, SRC_TOP0, 20, 1), MUX(0, "mout_aclk133", sclk_ampll_p4210, SRC_TOP0, 24, 1), - MUX(0, "mout_mixer", mout_mixer_p4210, SRC_TV, 4, 1), + MUX(CLK_MOUT_MIXER, "mout_mixer", mout_mixer_p4210, SRC_TV, 4, 1), MUX(0, "mout_dac", mout_dac_p4210, SRC_TV, 8, 1), MUX(0, "mout_g2d0", sclk_ampll_p4210, E4210_SRC_IMAGE, 0, 1), MUX(0, "mout_g2d1", sclk_evpll_p, E4210_SRC_IMAGE, 4, 1), @@ -719,7 +719,7 @@ static struct samsung_div_clock exynos4_div_clks[] __initdata = { DIV(0, "div_periph", "div_core2", DIV_CPU0, 12, 3), DIV(0, "div_atb", "mout_core", DIV_CPU0, 16, 3), DIV(0, "div_pclk_dbg", "div_atb", DIV_CPU0, 20, 3), - DIV(0, "div_core2", "div_core", DIV_CPU0, 28, 3), + DIV(CLK_ARM_CLK, "div_core2", "div_core", DIV_CPU0, 28, 3), DIV(0, "div_copy", "mout_hpm", DIV_CPU1, 0, 3), DIV(0, "div_hpm", "div_copy", DIV_CPU1, 4, 3), DIV(0, "div_clkout_cpu", "mout_clkout_cpu", CLKOUT_CMU_CPU, 8, 6), @@ -733,8 +733,7 @@ static struct samsung_div_clock exynos4_div_clks[] __initdata = { DIV(0, "div_csis0", "mout_csis0", DIV_CAM, 24, 4), DIV(0, "div_csis1", "mout_csis1", DIV_CAM, 28, 4), DIV(CLK_SCLK_MFC, "sclk_mfc", "mout_mfc", DIV_MFC, 0, 4), - DIV_F(0, "div_g3d", "mout_g3d", DIV_G3D, 0, 4, - CLK_SET_RATE_PARENT, 0), + DIV(CLK_SCLK_G3D, "sclk_g3d", "mout_g3d", DIV_G3D, 0, 4), DIV(0, "div_fimd0", "mout_fimd0", DIV_LCD0, 0, 4), DIV(0, "div_mipi0", "mout_mipi0", DIV_LCD0, 16, 4), DIV(0, "div_audio0", "mout_audio0", DIV_MAUDIO, 0, 4), @@ -769,7 +768,6 @@ static struct samsung_div_clock exynos4_div_clks[] __initdata = { DIV(0, "div_spi_pre2", "div_spi2", DIV_PERIL2, 8, 8), DIV(0, "div_audio1", "mout_audio1", DIV_PERIL4, 0, 4), DIV(0, "div_audio2", "mout_audio2", DIV_PERIL4, 16, 4), - DIV(CLK_ARM_CLK, "arm_clk", "div_core2", DIV_CPU0, 28, 3), DIV(CLK_SCLK_APLL, "sclk_apll", "mout_apll", DIV_CPU0, 24, 3), DIV_F(0, "div_mipi_pre0", "div_mipi0", DIV_LCD0, 20, 4, CLK_SET_RATE_PARENT, 0), @@ -857,8 +855,7 @@ static struct samsung_gate_clock exynos4_gate_clks[] __initdata = { 0), GATE(CLK_TSI, "tsi", "aclk133", GATE_IP_FSYS, 4, 0, 0), GATE(CLK_SROMC, "sromc", "aclk133", GATE_IP_FSYS, 11, 0, 0), - GATE(CLK_SCLK_G3D, "sclk_g3d", "div_g3d", GATE_IP_G3D, 0, - CLK_SET_RATE_PARENT, 0), + GATE(CLK_G3D, "g3d", "aclk200", GATE_IP_G3D, 0, 0, 0), GATE(CLK_PPMUG3D, "ppmug3d", "aclk200", GATE_IP_G3D, 1, 0, 0), GATE(CLK_USB_DEVICE, "usb_device", "aclk133", GATE_IP_FSYS, 13, 0, 0), GATE(CLK_ONENAND, "onenand", "aclk133", GATE_IP_FSYS, 15, 0, 0), @@ -1183,6 +1180,7 @@ static struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = { GATE(CLK_SPI1_ISP, "spi1_isp", "aclk200", E4X12_GATE_ISP1, 13, CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(CLK_G2D, "g2d", "aclk200", GATE_IP_DMC, 23, 0, 0), + GATE(CLK_SMMU_G2D, "smmu_g2d", "aclk200", GATE_IP_DMC, 24, 0, 0), GATE(CLK_TMU_APBIF, "tmu_apbif", "aclk100", E4X12_GATE_IP_PERIR, 17, 0, 0), }; @@ -1486,7 +1484,7 @@ static void __init exynos4_clk_init(struct device_node *np, exynos4_soc == EXYNOS4210 ? "Exynos4210" : "Exynos4x12", _get_rate("sclk_apll"), _get_rate("sclk_mpll"), _get_rate("sclk_epll"), _get_rate("sclk_vpll"), - _get_rate("arm_clk")); + _get_rate("div_core2")); } diff --git a/drivers/clk/samsung/clk-exynos5260.c b/drivers/clk/samsung/clk-exynos5260.c index ce3de97e5f11299fd1d852f8ec4f1d6612cbf571..2527e39aadcfbfafab2a0b5c97d4aab2e4809458 100644 --- a/drivers/clk/samsung/clk-exynos5260.c +++ b/drivers/clk/samsung/clk-exynos5260.c @@ -1581,7 +1581,7 @@ struct samsung_fixed_rate_clock fixed_rate_clks[] __initdata = { FRATE(PHYCLK_HDMI_LINK_O_TMDS_CLKHI, "phyclk_hdmi_link_o_tmds_clkhi", NULL, CLK_IS_ROOT, 125000000), FRATE(PHYCLK_MIPI_DPHY_4L_M_TXBYTECLKHS, - "phyclk_mipi_dphy_4l_m_txbyteclkhs" , NULL, + "phyclk_mipi_dphy_4l_m_txbyte_clkhs" , NULL, CLK_IS_ROOT, 187500000), FRATE(PHYCLK_DPTX_PHY_O_REF_CLK_24M, "phyclk_dptx_phy_o_ref_clk_24m", NULL, CLK_IS_ROOT, 24000000), diff --git a/drivers/clk/samsung/clk-s3c2410-dclk.c b/drivers/clk/samsung/clk-s3c2410-dclk.c index 0449cc0458ed0f8327b1bde2e4270a361be78143..f4f29ed6bd25a4d6d4cd0174738aaa93d6541353 100644 --- a/drivers/clk/samsung/clk-s3c2410-dclk.c +++ b/drivers/clk/samsung/clk-s3c2410-dclk.c @@ -426,7 +426,6 @@ MODULE_DEVICE_TABLE(platform, s3c24xx_dclk_driver_ids); static struct platform_driver s3c24xx_dclk_driver = { .driver = { .name = "s3c24xx-dclk", - .owner = THIS_MODULE, .pm = &s3c24xx_dclk_pm_ops, }, .probe = s3c24xx_dclk_probe, diff --git a/drivers/clk/samsung/clk-s3c2412.c b/drivers/clk/samsung/clk-s3c2412.c index 34af09f6a155745dc16ab2bcae4f008552a2753e..2ceedaf8ce18a6e411cc3e229f311805939535cd 100644 --- a/drivers/clk/samsung/clk-s3c2412.c +++ b/drivers/clk/samsung/clk-s3c2412.c @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -26,6 +27,7 @@ #define CLKCON 0x0c #define CLKDIVN 0x14 #define CLKSRC 0x1c +#define SWRST 0x30 /* list of PLLs to be registered */ enum s3c2412_plls { @@ -204,6 +206,28 @@ struct samsung_clock_alias s3c2412_aliases[] __initdata = { ALIAS(MSYSCLK, NULL, "fclk"), }; +static int s3c2412_restart(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + /* errata "Watch-dog/Software Reset Problem" specifies that + * this reset must be done with the SYSCLK sourced from + * EXTCLK instead of FOUT to avoid a glitch in the reset + * mechanism. + * + * See the watchdog section of the S3C2412 manual for more + * information on this fix. + */ + + __raw_writel(0x00, reg_base + CLKSRC); + __raw_writel(0x533C2412, reg_base + SWRST); + return NOTIFY_DONE; +} + +static struct notifier_block s3c2412_restart_handler = { + .notifier_call = s3c2412_restart, + .priority = 129, +}; + /* * fixed rate clocks generated outside the soc * Only necessary until the devicetree-move is complete @@ -233,6 +257,7 @@ void __init s3c2412_common_clk_init(struct device_node *np, unsigned long xti_f, unsigned long ext_f, void __iomem *base) { struct samsung_clk_provider *ctx; + int ret; reg_base = base; if (np) { @@ -267,6 +292,10 @@ void __init s3c2412_common_clk_init(struct device_node *np, unsigned long xti_f, s3c2412_clk_sleep_init(); samsung_clk_of_add_provider(np, ctx); + + ret = register_restart_handler(&s3c2412_restart_handler); + if (ret) + pr_warn("cannot register restart handler, %d\n", ret); } static void __init s3c2412_clk_init(struct device_node *np) diff --git a/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c index c92f853fca9f7ce76f562e76d912aae6004313cb..0c3c182b902af709313d69f72477b9f46f187e89 100644 --- a/drivers/clk/samsung/clk-s3c2443.c +++ b/drivers/clk/samsung/clk-s3c2443.c @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -33,6 +34,7 @@ #define HCLKCON 0x30 #define PCLKCON 0x34 #define SCLKCON 0x38 +#define SWRST 0x44 /* the soc types */ enum supported_socs { @@ -354,6 +356,18 @@ struct samsung_clock_alias s3c2450_aliases[] __initdata = { ALIAS(PCLK_I2C1, "s3c2410-i2c.1", "i2c"), }; +static int s3c2443_restart(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + __raw_writel(0x533c2443, reg_base + SWRST); + return NOTIFY_DONE; +} + +static struct notifier_block s3c2443_restart_handler = { + .notifier_call = s3c2443_restart, + .priority = 129, +}; + /* * fixed rate clocks generated outside the soc * Only necessary until the devicetree-move is complete @@ -378,6 +392,7 @@ void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f, void __iomem *base) { struct samsung_clk_provider *ctx; + int ret; reg_base = base; if (np) { @@ -447,6 +462,10 @@ void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f, s3c2443_clk_sleep_init(); samsung_clk_of_add_provider(np, ctx); + + ret = register_restart_handler(&s3c2443_restart_handler); + if (ret) + pr_warn("cannot register restart handler, %d\n", ret); } static void __init s3c2416_clk_init(struct device_node *np) diff --git a/drivers/clk/shmobile/Makefile b/drivers/clk/shmobile/Makefile index 531d4f6c70501e12aa19a1e6c4118a4a4fa1c615..960bf22d42ae156bda33c375ccfcbbb42bed8712 100644 --- a/drivers/clk/shmobile/Makefile +++ b/drivers/clk/shmobile/Makefile @@ -7,5 +7,3 @@ obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o obj-$(CONFIG_ARCH_R8A7794) += clk-rcar-gen2.o obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-div6.o obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-mstp.o -# for emply built-in.o -obj-n := dummy diff --git a/drivers/clk/shmobile/clk-rcar-gen2.c b/drivers/clk/shmobile/clk-rcar-gen2.c index dff7f79a19b9514cf4fff4091fd4d35b5ae11a30..e996425d06a920728cd4f32448c19d08efb5a776 100644 --- a/drivers/clk/shmobile/clk-rcar-gen2.c +++ b/drivers/clk/shmobile/clk-rcar-gen2.c @@ -202,6 +202,7 @@ static const struct clk_div_table cpg_sdh_div_table[] = { }; static const struct clk_div_table cpg_sd01_div_table[] = { + { 4, 8 }, { 5, 12 }, { 6, 16 }, { 7, 18 }, { 8, 24 }, { 10, 36 }, { 11, 48 }, { 12, 10 }, { 0, 0 }, }; diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index 6850cba35871a7b6a4fe7383c0412f2b74754786..7ddc2b5538466737fbdc0e05ca3c58a153094b26 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -5,6 +5,8 @@ obj-y += clk-sunxi.o clk-factors.o obj-y += clk-a10-hosc.o obj-y += clk-a20-gmac.o +obj-y += clk-mod0.o +obj-y += clk-sun8i-mbus.o obj-$(CONFIG_MFD_SUN6I_PRCM) += \ clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o \ diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c index 2057c8ac648f6a381fd137ce3b5db66dca8afe0a..f83ba097126c6e72c22517a66f17034474e8ebf2 100644 --- a/drivers/clk/sunxi/clk-factors.c +++ b/drivers/clk/sunxi/clk-factors.c @@ -9,18 +9,18 @@ */ #include +#include +#include +#include #include +#include #include -#include -#include #include -#include - #include "clk-factors.h" /* - * DOC: basic adjustable factor-based clock that cannot gate + * DOC: basic adjustable factor-based clock * * Traits of this clock: * prepare - clk_prepare only ensures that parents are prepared @@ -32,6 +32,8 @@ #define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) +#define FACTORS_MAX_PARENTS 5 + #define SETMASK(len, pos) (((1U << (len)) - 1) << (pos)) #define CLRMASK(len, pos) (~(SETMASK(len, pos))) #define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit)) @@ -147,9 +149,96 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -const struct clk_ops clk_factors_ops = { +static const struct clk_ops clk_factors_ops = { .determine_rate = clk_factors_determine_rate, .recalc_rate = clk_factors_recalc_rate, .round_rate = clk_factors_round_rate, .set_rate = clk_factors_set_rate, }; + +struct clk * __init sunxi_factors_register(struct device_node *node, + const struct factors_data *data, + spinlock_t *lock) +{ + struct clk *clk; + struct clk_factors *factors; + struct clk_gate *gate = NULL; + struct clk_mux *mux = NULL; + struct clk_hw *gate_hw = NULL; + struct clk_hw *mux_hw = NULL; + const char *clk_name = node->name; + const char *parents[FACTORS_MAX_PARENTS]; + void __iomem *reg; + int i = 0; + + reg = of_iomap(node, 0); + + /* if we have a mux, we will have >1 parents */ + while (i < FACTORS_MAX_PARENTS && + (parents[i] = of_clk_get_parent_name(node, i)) != NULL) + i++; + + /* + * some factor clocks, such as pll5 and pll6, may have multiple + * outputs, and have their name designated in factors_data + */ + if (data->name) + clk_name = data->name; + else + of_property_read_string(node, "clock-output-names", &clk_name); + + factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); + if (!factors) + return NULL; + + /* set up factors properties */ + factors->reg = reg; + factors->config = data->table; + factors->get_factors = data->getter; + factors->lock = lock; + + /* Add a gate if this factor clock can be gated */ + if (data->enable) { + gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); + if (!gate) { + kfree(factors); + return NULL; + } + + /* set up gate properties */ + gate->reg = reg; + gate->bit_idx = data->enable; + gate->lock = factors->lock; + gate_hw = &gate->hw; + } + + /* Add a mux if this factor clock can be muxed */ + if (data->mux) { + mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); + if (!mux) { + kfree(factors); + kfree(gate); + return NULL; + } + + /* set up gate properties */ + mux->reg = reg; + mux->shift = data->mux; + mux->mask = SUNXI_FACTORS_MUX_MASK; + mux->lock = factors->lock; + mux_hw = &mux->hw; + } + + clk = clk_register_composite(NULL, clk_name, + parents, i, + mux_hw, &clk_mux_ops, + &factors->hw, &clk_factors_ops, + gate_hw, &clk_gate_ops, 0); + + if (!IS_ERR(clk)) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } + + return clk; +} diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h index d2d0efa39379985f284387d87bba2159627480b1..9913840018d3fdb4f4717a4636b68f8dc6f4a6e6 100644 --- a/drivers/clk/sunxi/clk-factors.h +++ b/drivers/clk/sunxi/clk-factors.h @@ -3,9 +3,12 @@ #include #include +#include #define SUNXI_FACTORS_NOT_APPLICABLE (0) +#define SUNXI_FACTORS_MUX_MASK 0x3 + struct clk_factors_config { u8 nshift; u8 nwidth; @@ -18,6 +21,14 @@ struct clk_factors_config { u8 n_start; }; +struct factors_data { + int enable; + int mux; + struct clk_factors_config *table; + void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); + const char *name; +}; + struct clk_factors { struct clk_hw hw; void __iomem *reg; @@ -26,5 +37,8 @@ struct clk_factors { spinlock_t *lock; }; -extern const struct clk_ops clk_factors_ops; +struct clk * __init sunxi_factors_register(struct device_node *node, + const struct factors_data *data, + spinlock_t *lock); + #endif diff --git a/drivers/clk/sunxi/clk-mod0.c b/drivers/clk/sunxi/clk-mod0.c new file mode 100644 index 0000000000000000000000000000000000000000..4a563850ee6ee3a158a286b8a1cfb37ed713569b --- /dev/null +++ b/drivers/clk/sunxi/clk-mod0.c @@ -0,0 +1,283 @@ +/* + * Copyright 2013 Emilio López + * + * Emilio López + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include "clk-factors.h" + +/** + * sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks + * MOD0 rate is calculated as follows + * rate = (parent_rate >> p) / (m + 1); + */ + +static void sun4i_a10_get_mod0_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 div, calcm, calcp; + + /* These clocks can only divide, so we will never be able to achieve + * frequencies higher than the parent frequency */ + if (*freq > parent_rate) + *freq = parent_rate; + + div = DIV_ROUND_UP(parent_rate, *freq); + + if (div < 16) + calcp = 0; + else if (div / 2 < 16) + calcp = 1; + else if (div / 4 < 16) + calcp = 2; + else + calcp = 3; + + calcm = DIV_ROUND_UP(div, 1 << calcp); + + *freq = (parent_rate >> calcp) / calcm; + + /* we were called to round the frequency, we can now return */ + if (n == NULL) + return; + + *m = calcm - 1; + *p = calcp; +} + +/* user manual says "n" but it's really "p" */ +static struct clk_factors_config sun4i_a10_mod0_config = { + .mshift = 0, + .mwidth = 4, + .pshift = 16, + .pwidth = 2, +}; + +static const struct factors_data sun4i_a10_mod0_data __initconst = { + .enable = 31, + .mux = 24, + .table = &sun4i_a10_mod0_config, + .getter = sun4i_a10_get_mod0_factors, +}; + +static DEFINE_SPINLOCK(sun4i_a10_mod0_lock); + +static void __init sun4i_a10_mod0_setup(struct device_node *node) +{ + sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun4i_a10_mod0_lock); +} +CLK_OF_DECLARE(sun4i_a10_mod0, "allwinner,sun4i-a10-mod0-clk", sun4i_a10_mod0_setup); + +static DEFINE_SPINLOCK(sun5i_a13_mbus_lock); + +static void __init sun5i_a13_mbus_setup(struct device_node *node) +{ + struct clk *mbus = sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun5i_a13_mbus_lock); + + /* The MBUS clocks needs to be always enabled */ + __clk_get(mbus); + clk_prepare_enable(mbus); +} +CLK_OF_DECLARE(sun5i_a13_mbus, "allwinner,sun5i-a13-mbus-clk", sun5i_a13_mbus_setup); + +struct mmc_phase_data { + u8 offset; +}; + +struct mmc_phase { + struct clk_hw hw; + void __iomem *reg; + struct mmc_phase_data *data; + spinlock_t *lock; +}; + +#define to_mmc_phase(_hw) container_of(_hw, struct mmc_phase, hw) + +static int mmc_get_phase(struct clk_hw *hw) +{ + struct clk *mmc, *mmc_parent, *clk = hw->clk; + struct mmc_phase *phase = to_mmc_phase(hw); + unsigned int mmc_rate, mmc_parent_rate; + u16 step, mmc_div; + u32 value; + u8 delay; + + value = readl(phase->reg); + delay = (value >> phase->data->offset) & 0x3; + + if (!delay) + return 180; + + /* Get the main MMC clock */ + mmc = clk_get_parent(clk); + if (!mmc) + return -EINVAL; + + /* And its rate */ + mmc_rate = clk_get_rate(mmc); + if (!mmc_rate) + return -EINVAL; + + /* Now, get the MMC parent (most likely some PLL) */ + mmc_parent = clk_get_parent(mmc); + if (!mmc_parent) + return -EINVAL; + + /* And its rate */ + mmc_parent_rate = clk_get_rate(mmc_parent); + if (!mmc_parent_rate) + return -EINVAL; + + /* Get MMC clock divider */ + mmc_div = mmc_parent_rate / mmc_rate; + + step = DIV_ROUND_CLOSEST(360, mmc_div); + return delay * step; +} + +static int mmc_set_phase(struct clk_hw *hw, int degrees) +{ + struct clk *mmc, *mmc_parent, *clk = hw->clk; + struct mmc_phase *phase = to_mmc_phase(hw); + unsigned int mmc_rate, mmc_parent_rate; + unsigned long flags; + u32 value; + u8 delay; + + /* Get the main MMC clock */ + mmc = clk_get_parent(clk); + if (!mmc) + return -EINVAL; + + /* And its rate */ + mmc_rate = clk_get_rate(mmc); + if (!mmc_rate) + return -EINVAL; + + /* Now, get the MMC parent (most likely some PLL) */ + mmc_parent = clk_get_parent(mmc); + if (!mmc_parent) + return -EINVAL; + + /* And its rate */ + mmc_parent_rate = clk_get_rate(mmc_parent); + if (!mmc_parent_rate) + return -EINVAL; + + if (degrees != 180) { + u16 step, mmc_div; + + /* Get MMC clock divider */ + mmc_div = mmc_parent_rate / mmc_rate; + + /* + * We can only outphase the clocks by multiple of the + * PLL's period. + * + * Since the MMC clock in only a divider, and the + * formula to get the outphasing in degrees is deg = + * 360 * delta / period + * + * If we simplify this formula, we can see that the + * only thing that we're concerned about is the number + * of period we want to outphase our clock from, and + * the divider set by the MMC clock. + */ + step = DIV_ROUND_CLOSEST(360, mmc_div); + delay = DIV_ROUND_CLOSEST(degrees, step); + } else { + delay = 0; + } + + spin_lock_irqsave(phase->lock, flags); + value = readl(phase->reg); + value &= ~GENMASK(phase->data->offset + 3, phase->data->offset); + value |= delay << phase->data->offset; + writel(value, phase->reg); + spin_unlock_irqrestore(phase->lock, flags); + + return 0; +} + +static const struct clk_ops mmc_clk_ops = { + .get_phase = mmc_get_phase, + .set_phase = mmc_set_phase, +}; + +static void __init sun4i_a10_mmc_phase_setup(struct device_node *node, + struct mmc_phase_data *data) +{ + const char *parent_names[1] = { of_clk_get_parent_name(node, 0) }; + struct clk_init_data init = { + .num_parents = 1, + .parent_names = parent_names, + .ops = &mmc_clk_ops, + }; + + struct mmc_phase *phase; + struct clk *clk; + + phase = kmalloc(sizeof(*phase), GFP_KERNEL); + if (!phase) + return; + + phase->hw.init = &init; + + phase->reg = of_iomap(node, 0); + if (!phase->reg) + goto err_free; + + phase->data = data; + phase->lock = &sun4i_a10_mod0_lock; + + if (of_property_read_string(node, "clock-output-names", &init.name)) + init.name = node->name; + + clk = clk_register(NULL, &phase->hw); + if (IS_ERR(clk)) + goto err_unmap; + + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return; + +err_unmap: + iounmap(phase->reg); +err_free: + kfree(phase); +} + + +static struct mmc_phase_data mmc_output_clk = { + .offset = 8, +}; + +static struct mmc_phase_data mmc_sample_clk = { + .offset = 20, +}; + +static void __init sun4i_a10_mmc_output_setup(struct device_node *node) +{ + sun4i_a10_mmc_phase_setup(node, &mmc_output_clk); +} +CLK_OF_DECLARE(sun4i_a10_mmc_output, "allwinner,sun4i-a10-mmc-output-clk", sun4i_a10_mmc_output_setup); + +static void __init sun4i_a10_mmc_sample_setup(struct device_node *node) +{ + sun4i_a10_mmc_phase_setup(node, &mmc_sample_clk); +} +CLK_OF_DECLARE(sun4i_a10_mmc_sample, "allwinner,sun4i-a10-mmc-sample-clk", sun4i_a10_mmc_sample_setup); diff --git a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c index e10d0521ec768532245dc096a03774cb6ae907b4..64f3e46d383cc2ede89889cb7b2d4e9b26d0a22a 100644 --- a/drivers/clk/sunxi/clk-sun6i-apb0-gates.c +++ b/drivers/clk/sunxi/clk-sun6i-apb0-gates.c @@ -99,7 +99,6 @@ static int sun6i_a31_apb0_gates_clk_probe(struct platform_device *pdev) static struct platform_driver sun6i_a31_apb0_gates_clk_driver = { .driver = { .name = "sun6i-a31-apb0-gates-clk", - .owner = THIS_MODULE, .of_match_table = sun6i_a31_apb0_gates_clk_dt_ids, }, .probe = sun6i_a31_apb0_gates_clk_probe, diff --git a/drivers/clk/sunxi/clk-sun6i-apb0.c b/drivers/clk/sunxi/clk-sun6i-apb0.c index 1fa23371c8c6e3fb25fb14de5dc6d8b325392020..70763600aeaeed91e73203fdc9a1e68efc5a97b2 100644 --- a/drivers/clk/sunxi/clk-sun6i-apb0.c +++ b/drivers/clk/sunxi/clk-sun6i-apb0.c @@ -65,7 +65,6 @@ static const struct of_device_id sun6i_a31_apb0_clk_dt_ids[] = { static struct platform_driver sun6i_a31_apb0_clk_driver = { .driver = { .name = "sun6i-a31-apb0-clk", - .owner = THIS_MODULE, .of_match_table = sun6i_a31_apb0_clk_dt_ids, }, .probe = sun6i_a31_apb0_clk_probe, diff --git a/drivers/clk/sunxi/clk-sun6i-ar100.c b/drivers/clk/sunxi/clk-sun6i-ar100.c index eca8ca025b6aa0f01a2472fba41c5bd811e4a6a1..acca53290be26c8f5c6aea33ee92b7d7f590703d 100644 --- a/drivers/clk/sunxi/clk-sun6i-ar100.c +++ b/drivers/clk/sunxi/clk-sun6i-ar100.c @@ -221,7 +221,6 @@ static const struct of_device_id sun6i_a31_ar100_clk_dt_ids[] = { static struct platform_driver sun6i_a31_ar100_clk_driver = { .driver = { .name = "sun6i-a31-ar100-clk", - .owner = THIS_MODULE, .of_match_table = sun6i_a31_ar100_clk_dt_ids, }, .probe = sun6i_a31_ar100_clk_probe, diff --git a/drivers/clk/sunxi/clk-sun8i-apb0.c b/drivers/clk/sunxi/clk-sun8i-apb0.c index 1f5ba9b4b8cd085b21fdeb7c6ee48d696de6d34c..155d0022194f2665068b46ed92a0b70cefbca601 100644 --- a/drivers/clk/sunxi/clk-sun8i-apb0.c +++ b/drivers/clk/sunxi/clk-sun8i-apb0.c @@ -56,7 +56,6 @@ static const struct of_device_id sun8i_a23_apb0_clk_dt_ids[] = { static struct platform_driver sun8i_a23_apb0_clk_driver = { .driver = { .name = "sun8i-a23-apb0-clk", - .owner = THIS_MODULE, .of_match_table = sun8i_a23_apb0_clk_dt_ids, }, .probe = sun8i_a23_apb0_clk_probe, diff --git a/drivers/clk/sunxi/clk-sun8i-mbus.c b/drivers/clk/sunxi/clk-sun8i-mbus.c new file mode 100644 index 0000000000000000000000000000000000000000..8e49b44cee41d9df81ffa666cf812a3fffcdd7af --- /dev/null +++ b/drivers/clk/sunxi/clk-sun8i-mbus.c @@ -0,0 +1,78 @@ +/* + * Copyright 2014 Chen-Yu Tsai + * + * Chen-Yu Tsai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include "clk-factors.h" + +/** + * sun8i_a23_get_mbus_factors() - calculates m factor for MBUS clocks + * MBUS rate is calculated as follows + * rate = parent_rate / (m + 1); + */ + +static void sun8i_a23_get_mbus_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 div; + + /* + * These clocks can only divide, so we will never be able to + * achieve frequencies higher than the parent frequency + */ + if (*freq > parent_rate) + *freq = parent_rate; + + div = DIV_ROUND_UP(parent_rate, *freq); + + if (div > 8) + div = 8; + + *freq = parent_rate / div; + + /* we were called to round the frequency, we can now return */ + if (m == NULL) + return; + + *m = div - 1; +} + +static struct clk_factors_config sun8i_a23_mbus_config = { + .mshift = 0, + .mwidth = 3, +}; + +static const struct factors_data sun8i_a23_mbus_data __initconst = { + .enable = 31, + .mux = 24, + .table = &sun8i_a23_mbus_config, + .getter = sun8i_a23_get_mbus_factors, +}; + +static DEFINE_SPINLOCK(sun8i_a23_mbus_lock); + +static void __init sun8i_a23_mbus_setup(struct device_node *node) +{ + struct clk *mbus = sunxi_factors_register(node, &sun8i_a23_mbus_data, + &sun8i_a23_mbus_lock); + + /* The MBUS clocks needs to be always enabled */ + __clk_get(mbus); + clk_prepare_enable(mbus); +} +CLK_OF_DECLARE(sun8i_a23_mbus, "allwinner,sun8i-a23-mbus-clk", sun8i_a23_mbus_setup); diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index b654b7b1d137b22c81614bdfd274c9d860144582..d5dc951264cab957bcf2522bc61a9c62395c6432 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "clk-factors.h" @@ -319,46 +320,6 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate, -/** - * sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks - * MOD0 rate is calculated as follows - * rate = (parent_rate >> p) / (m + 1); - */ - -static void sun4i_get_mod0_factors(u32 *freq, u32 parent_rate, - u8 *n, u8 *k, u8 *m, u8 *p) -{ - u8 div, calcm, calcp; - - /* These clocks can only divide, so we will never be able to achieve - * frequencies higher than the parent frequency */ - if (*freq > parent_rate) - *freq = parent_rate; - - div = DIV_ROUND_UP(parent_rate, *freq); - - if (div < 16) - calcp = 0; - else if (div / 2 < 16) - calcp = 1; - else if (div / 4 < 16) - calcp = 2; - else - calcp = 3; - - calcm = DIV_ROUND_UP(div, 1 << calcp); - - *freq = (parent_rate >> calcp) / calcm; - - /* we were called to round the frequency, we can now return */ - if (n == NULL) - return; - - *m = calcm - 1; - *p = calcp; -} - - /** * sun7i_a20_get_out_factors() - calculates m, p factors for CLK_OUT_A/B @@ -440,16 +401,6 @@ EXPORT_SYMBOL(clk_sunxi_mmc_phase_control); * sunxi_factors_clk_setup() - Setup function for factor clocks */ -#define SUNXI_FACTORS_MUX_MASK 0x3 - -struct factors_data { - int enable; - int mux; - struct clk_factors_config *table; - void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); - const char *name; -}; - static struct clk_factors_config sun4i_pll1_config = { .nshift = 8, .nwidth = 5, @@ -503,14 +454,6 @@ static struct clk_factors_config sun4i_apb1_config = { .pwidth = 2, }; -/* user manual says "n" but it's really "p" */ -static struct clk_factors_config sun4i_mod0_config = { - .mshift = 0, - .mwidth = 4, - .pshift = 16, - .pwidth = 2, -}; - /* user manual says "n" but it's really "p" */ static struct clk_factors_config sun7i_a20_out_config = { .mshift = 8, @@ -568,13 +511,6 @@ static const struct factors_data sun4i_apb1_data __initconst = { .getter = sun4i_get_apb1_factors, }; -static const struct factors_data sun4i_mod0_data __initconst = { - .enable = 31, - .mux = 24, - .table = &sun4i_mod0_config, - .getter = sun4i_get_mod0_factors, -}; - static const struct factors_data sun7i_a20_out_data __initconst = { .enable = 31, .mux = 24, @@ -583,89 +519,9 @@ static const struct factors_data sun7i_a20_out_data __initconst = { }; static struct clk * __init sunxi_factors_clk_setup(struct device_node *node, - const struct factors_data *data) + const struct factors_data *data) { - struct clk *clk; - struct clk_factors *factors; - struct clk_gate *gate = NULL; - struct clk_mux *mux = NULL; - struct clk_hw *gate_hw = NULL; - struct clk_hw *mux_hw = NULL; - const char *clk_name = node->name; - const char *parents[SUNXI_MAX_PARENTS]; - void __iomem *reg; - int i = 0; - - reg = of_iomap(node, 0); - - /* if we have a mux, we will have >1 parents */ - while (i < SUNXI_MAX_PARENTS && - (parents[i] = of_clk_get_parent_name(node, i)) != NULL) - i++; - - /* - * some factor clocks, such as pll5 and pll6, may have multiple - * outputs, and have their name designated in factors_data - */ - if (data->name) - clk_name = data->name; - else - of_property_read_string(node, "clock-output-names", &clk_name); - - factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); - if (!factors) - return NULL; - - /* Add a gate if this factor clock can be gated */ - if (data->enable) { - gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); - if (!gate) { - kfree(factors); - return NULL; - } - - /* set up gate properties */ - gate->reg = reg; - gate->bit_idx = data->enable; - gate->lock = &clk_lock; - gate_hw = &gate->hw; - } - - /* Add a mux if this factor clock can be muxed */ - if (data->mux) { - mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); - if (!mux) { - kfree(factors); - kfree(gate); - return NULL; - } - - /* set up gate properties */ - mux->reg = reg; - mux->shift = data->mux; - mux->mask = SUNXI_FACTORS_MUX_MASK; - mux->lock = &clk_lock; - mux_hw = &mux->hw; - } - - /* set up factors properties */ - factors->reg = reg; - factors->config = data->table; - factors->get_factors = data->getter; - factors->lock = &clk_lock; - - clk = clk_register_composite(NULL, clk_name, - parents, i, - mux_hw, &clk_mux_ops, - &factors->hw, &clk_factors_ops, - gate_hw, &clk_gate_ops, 0); - - if (!IS_ERR(clk)) { - of_clk_add_provider(node, of_clk_src_simple_get, clk); - clk_register_clkdev(clk, clk_name, NULL); - } - - return clk; + return sunxi_factors_register(node, data, &clk_lock); } @@ -762,10 +618,19 @@ static const struct div_data sun4i_ahb_data __initconst = { .width = 2, }; +static const struct clk_div_table sun4i_apb0_table[] __initconst = { + { .val = 0, .div = 2 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { } /* sentinel */ +}; + static const struct div_data sun4i_apb0_data __initconst = { .shift = 8, .pow = 1, .width = 2, + .table = sun4i_apb0_table, }; static const struct div_data sun6i_a31_apb2_div_data __initconst = { @@ -1199,7 +1064,6 @@ static const struct of_device_id clk_factors_match[] __initconst = { {.compatible = "allwinner,sun7i-a20-pll4-clk", .data = &sun7i_a20_pll4_data,}, {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,}, {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,}, - {.compatible = "allwinner,sun4i-a10-mod0-clk", .data = &sun4i_mod0_data,}, {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,}, {} }; @@ -1311,7 +1175,6 @@ static void __init sun4i_a10_init_clocks(struct device_node *node) CLK_OF_DECLARE(sun4i_a10_clk_init, "allwinner,sun4i-a10", sun4i_a10_init_clocks); static const char *sun5i_critical_clocks[] __initdata = { - "mbus", "pll5_ddr", "ahb_sdram", }; diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index 9525c684d14930267fbf00ec634550e52469a373..e3a85842ce0c05890351f49f4d4a6c330812b9ab 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -1166,6 +1166,12 @@ static void __init tegra124_pll_init(void __iomem *clk_base, clk_register_clkdev(clk, "pll_c_out1", NULL); clks[TEGRA124_CLK_PLL_C_OUT1] = clk; + /* PLLC_UD */ + clk = clk_register_fixed_factor(NULL, "pll_c_ud", "pll_c", + CLK_SET_RATE_PARENT, 1, 1); + clk_register_clkdev(clk, "pll_c_ud", NULL); + clks[TEGRA124_CLK_PLL_C_UD] = clk; + /* PLLC2 */ clk = tegra_clk_register_pllc("pll_c2", "pll_ref", clk_base, pmc, 0, &pll_c2_params, NULL); @@ -1198,6 +1204,8 @@ static void __init tegra124_pll_init(void __iomem *clk_base, /* PLLM_UD */ clk = clk_register_fixed_factor(NULL, "pll_m_ud", "pll_m", CLK_SET_RATE_PARENT, 1, 1); + clk_register_clkdev(clk, "pll_m_ud", NULL); + clks[TEGRA124_CLK_PLL_M_UD] = clk; /* PLLU */ val = readl(clk_base + pll_u_params.base_reg); diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index f87c609e8f727149c5402e55508815801f9e186c..97dc8595c3cd5c1b59113deaa16bbd23a5298cfc 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -207,8 +207,13 @@ void __init tegra_init_from_table(struct tegra_clk_init_table *tbl, for (; tbl->clk_id < clk_max; tbl++) { clk = clks[tbl->clk_id]; - if (IS_ERR_OR_NULL(clk)) - return; + if (IS_ERR_OR_NULL(clk)) { + pr_err("%s: invalid entry %ld in clks array for id %d\n", + __func__, PTR_ERR(clk), tbl->clk_id); + WARN_ON(1); + + continue; + } if (tbl->parent_id < clk_max) { struct clk *parent = clks[tbl->parent_id]; diff --git a/drivers/clk/ti/clk-dra7-atl.c b/drivers/clk/ti/clk-dra7-atl.c index af29359677da3ad3e42d75e47bf0dfecc0621249..59bb4b39d12e799f6d32d8f5a3d898e4dc9d1c46 100644 --- a/drivers/clk/ti/clk-dra7-atl.c +++ b/drivers/clk/ti/clk-dra7-atl.c @@ -203,6 +203,7 @@ static void __init of_dra7_atl_clock_setup(struct device_node *node) if (!IS_ERR(clk)) { of_clk_add_provider(node, of_clk_src_simple_get, clk); + kfree(parent_names); return; } cleanup: @@ -228,6 +229,7 @@ static int of_dra7_atl_clk_probe(struct platform_device *pdev) cinfo->iobase = of_iomap(node, 0); cinfo->dev = &pdev->dev; pm_runtime_enable(cinfo->dev); + pm_runtime_irq_safe(cinfo->dev); pm_runtime_get_sync(cinfo->dev); atl_write(cinfo, DRA7_ATL_PCLKMUX_REG(0), DRA7_ATL_PCLKMUX); @@ -301,7 +303,6 @@ MODULE_DEVICE_TABLE(of, of_dra7_atl_clk_match_tbl); static struct platform_driver dra7_atl_clk_driver = { .driver = { .name = "dra7-atl", - .owner = THIS_MODULE, .of_match_table = of_dra7_atl_clk_match_tbl, }, .probe = of_dra7_atl_clk_probe, diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c index b1a6f7144f3fcb10e96608b5247b36f227f04600..337abe5909e1f272cdcdca85e5ae0fb3fe1f4729 100644 --- a/drivers/clk/ti/clk.c +++ b/drivers/clk/ti/clk.c @@ -25,8 +25,8 @@ #undef pr_fmt #define pr_fmt(fmt) "%s: " fmt, __func__ -static int ti_dt_clk_memmap_index; struct ti_clk_ll_ops *ti_clk_ll_ops; +static struct device_node *clocks_node_ptr[CLK_MAX_MEMMAPS]; /** * ti_dt_clocks_register - register DT alias clocks during boot @@ -108,9 +108,21 @@ void __iomem *ti_clk_get_reg_addr(struct device_node *node, int index) struct clk_omap_reg *reg; u32 val; u32 tmp; + int i; reg = (struct clk_omap_reg *)&tmp; - reg->index = ti_dt_clk_memmap_index; + + for (i = 0; i < CLK_MAX_MEMMAPS; i++) { + if (clocks_node_ptr[i] == node->parent) + break; + } + + if (i == CLK_MAX_MEMMAPS) { + pr_err("clk-provider not found for %s!\n", node->name); + return NULL; + } + + reg->index = i; if (of_property_read_u32_index(node, "reg", index, &val)) { pr_err("%s must have reg[%d]!\n", node->name, index); @@ -127,20 +139,14 @@ void __iomem *ti_clk_get_reg_addr(struct device_node *node, int index) * @parent: master node * @index: internal index for clk_reg_ops * - * Initializes a master clock IP block and its child clock nodes. - * Regmap is provided for accessing the register space for the - * IP block and all the clocks under it. + * Initializes a master clock IP block. This basically sets up the + * mapping from clocks node to the memory map index. All the clocks + * are then initialized through the common of_clk_init call, and the + * clocks will access their memory maps based on the node layout. */ void ti_dt_clk_init_provider(struct device_node *parent, int index) { - const struct of_device_id *match; - struct device_node *np; struct device_node *clocks; - of_clk_init_cb_t clk_init_cb; - struct clk_init_item *retry; - struct clk_init_item *tmp; - - ti_dt_clk_memmap_index = index; /* get clocks for this parent */ clocks = of_get_child_by_name(parent, "clocks"); @@ -149,19 +155,31 @@ void ti_dt_clk_init_provider(struct device_node *parent, int index) return; } - for_each_child_of_node(clocks, np) { - match = of_match_node(&__clk_of_table, np); - if (!match) - continue; - clk_init_cb = (of_clk_init_cb_t)match->data; - pr_debug("%s: initializing: %s\n", __func__, np->name); - clk_init_cb(np); - } + /* add clocks node info */ + clocks_node_ptr[index] = clocks; +} - list_for_each_entry_safe(retry, tmp, &retry_list, link) { - pr_debug("retry-init: %s\n", retry->node->name); - retry->func(retry->hw, retry->node); - list_del(&retry->link); - kfree(retry); +/** + * ti_dt_clk_init_retry_clks - init clocks from the retry list + * + * Initializes any clocks that have failed to initialize before, + * reasons being missing parent node(s) during earlier init. This + * typically happens only for DPLLs which need to have both of their + * parent clocks ready during init. + */ +void ti_dt_clk_init_retry_clks(void) +{ + struct clk_init_item *retry; + struct clk_init_item *tmp; + int retries = 5; + + while (!list_empty(&retry_list) && retries) { + list_for_each_entry_safe(retry, tmp, &retry_list, link) { + pr_debug("retry-init: %s\n", retry->node->name); + retry->func(retry->hw, retry->node); + list_del(&retry->link); + kfree(retry); + } + retries--; } } diff --git a/drivers/clk/ti/clockdomain.c b/drivers/clk/ti/clockdomain.c index f1e0038d76acd34028764ab52fbfe8d511a4a1ae..b4c5faccaece634c4c9459c6be311f64e4daaf45 100644 --- a/drivers/clk/ti/clockdomain.c +++ b/drivers/clk/ti/clockdomain.c @@ -36,6 +36,11 @@ static void __init of_ti_clockdomain_setup(struct device_node *node) for (i = 0; i < num_clks; i++) { clk = of_clk_get(node, i); + if (IS_ERR(clk)) { + pr_err("%s: Failed get %s' clock nr %d (%ld)\n", + __func__, node->full_name, i, PTR_ERR(clk)); + continue; + } if (__clk_get_flags(clk) & CLK_IS_BASIC) { pr_warn("can't setup clkdm for basic clk %s\n", __clk_get_name(clk)); diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c index a837f703be658304b7d2a3a0e2743bd17d7077a7..bff2b5b8ff598b2e150496eb3ebc984c651a0ebd 100644 --- a/drivers/clk/ti/divider.c +++ b/drivers/clk/ti/divider.c @@ -300,8 +300,8 @@ static struct clk *_register_divider(struct device *dev, const char *name, return clk; } -static struct clk_div_table -__init *ti_clk_get_div_table(struct device_node *node) +static struct clk_div_table * +__init ti_clk_get_div_table(struct device_node *node) { struct clk_div_table *table; const __be32 *divspec; diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index 246cf1226eaaec10e028a8fba5a0942667762a42..9037bebd69f79cd9121d101913bb34d0cffa4420 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -85,24 +85,22 @@ static DEFINE_SPINLOCK(canmioclk_lock); static DEFINE_SPINLOCK(dbgclk_lock); static DEFINE_SPINLOCK(aperclk_lock); -static const char dummy_nm[] __initconst = "dummy_name"; - -static const char *armpll_parents[] __initdata = {"armpll_int", "ps_clk"}; -static const char *ddrpll_parents[] __initdata = {"ddrpll_int", "ps_clk"}; -static const char *iopll_parents[] __initdata = {"iopll_int", "ps_clk"}; -static const char *gem0_mux_parents[] __initdata = {"gem0_div1", dummy_nm}; -static const char *gem1_mux_parents[] __initdata = {"gem1_div1", dummy_nm}; -static const char *can0_mio_mux2_parents[] __initdata = {"can0_gate", +static const char *armpll_parents[] __initconst = {"armpll_int", "ps_clk"}; +static const char *ddrpll_parents[] __initconst = {"ddrpll_int", "ps_clk"}; +static const char *iopll_parents[] __initconst = {"iopll_int", "ps_clk"}; +static const char *gem0_mux_parents[] __initconst = {"gem0_div1", "dummy_name"}; +static const char *gem1_mux_parents[] __initconst = {"gem1_div1", "dummy_name"}; +static const char *can0_mio_mux2_parents[] __initconst = {"can0_gate", "can0_mio_mux"}; -static const char *can1_mio_mux2_parents[] __initdata = {"can1_gate", +static const char *can1_mio_mux2_parents[] __initconst = {"can1_gate", "can1_mio_mux"}; -static const char *dbg_emio_mux_parents[] __initdata = {"dbg_div", - dummy_nm}; +static const char *dbg_emio_mux_parents[] __initconst = {"dbg_div", + "dummy_name"}; -static const char *dbgtrc_emio_input_names[] __initdata = {"trace_emio_clk"}; -static const char *gem0_emio_input_names[] __initdata = {"gem0_emio_clk"}; -static const char *gem1_emio_input_names[] __initdata = {"gem1_emio_clk"}; -static const char *swdt_ext_clk_input_names[] __initdata = {"swdt_ext_clk"}; +static const char *dbgtrc_emio_input_names[] __initconst = {"trace_emio_clk"}; +static const char *gem0_emio_input_names[] __initconst = {"gem0_emio_clk"}; +static const char *gem1_emio_input_names[] __initconst = {"gem1_emio_clk"}; +static const char *swdt_ext_clk_input_names[] __initconst = {"swdt_ext_clk"}; static void __init zynq_clk_register_fclk(enum zynq_clk fclk, const char *clk_name, void __iomem *fclk_ctrl_reg, @@ -230,6 +228,7 @@ static void __init zynq_clk_setup(struct device_node *np) const char *periph_parents[4]; const char *swdt_ext_clk_mux_parents[2]; const char *can_mio_mux_parents[NUM_MIO_PINS]; + const char *dummy_nm = "dummy_name"; pr_info("Zynq clock init\n"); @@ -619,5 +618,4 @@ void __init zynq_clock_init(void) np_err: of_node_put(np); BUG(); - return; } diff --git a/drivers/clk/zynq/pll.c b/drivers/clk/zynq/pll.c index cec97596fe65988ff473e7f6d08302a7f68ba375..00d72fb5c0361e675c3930746588c19d860165da 100644 --- a/drivers/clk/zynq/pll.c +++ b/drivers/clk/zynq/pll.c @@ -211,10 +211,8 @@ struct clk *clk_register_zynq_pll(const char *name, const char *parent, }; pll = kmalloc(sizeof(*pll), GFP_KERNEL); - if (!pll) { - pr_err("%s: Could not allocate Zynq PLL clk.\n", __func__); + if (!pll) return ERR_PTR(-ENOMEM); - } /* Populate the struct */ pll->hw.init = &initd; diff --git a/drivers/clocksource/dummy_timer.c b/drivers/clocksource/dummy_timer.c index ad357254172890ff4170c2a32634f57ad831b26e..31990600fcff3fc5bacb22f0d03a1e5cde1e2d4a 100644 --- a/drivers/clocksource/dummy_timer.c +++ b/drivers/clocksource/dummy_timer.c @@ -28,7 +28,7 @@ static void dummy_timer_set_mode(enum clock_event_mode mode, static void dummy_timer_setup(void) { int cpu = smp_processor_id(); - struct clock_event_device *evt = __this_cpu_ptr(&dummy_timer_evt); + struct clock_event_device *evt = raw_cpu_ptr(&dummy_timer_evt); evt->name = "dummy_timer"; evt->features = CLOCK_EVT_FEAT_PERIODIC | diff --git a/drivers/clocksource/metag_generic.c b/drivers/clocksource/metag_generic.c index 9e4db41abe3ccc0a84b350e159f7fa2ed6b001b2..b7384b853e5a8203534d398f3adb7ec20089f7cc 100644 --- a/drivers/clocksource/metag_generic.c +++ b/drivers/clocksource/metag_generic.c @@ -90,7 +90,7 @@ static struct clocksource clocksource_metag = { static irqreturn_t metag_timer_interrupt(int irq, void *dummy) { - struct clock_event_device *evt = &__get_cpu_var(local_clockevent); + struct clock_event_device *evt = this_cpu_ptr(&local_clockevent); evt->event_handler(evt); diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c index 8d115db1e6510044dfe10fc0e87e949f517fca43..098c542e5c537dca3f159771960b453d61b578a8 100644 --- a/drivers/clocksource/qcom-timer.c +++ b/drivers/clocksource/qcom-timer.c @@ -219,7 +219,7 @@ static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq, } /* Immediately configure the timer on the boot CPU */ - msm_local_timer_setup(__this_cpu_ptr(msm_evt)); + msm_local_timer_setup(raw_cpu_ptr(msm_evt)); } err: diff --git a/drivers/cpufreq/kirkwood-cpufreq.c b/drivers/cpufreq/kirkwood-cpufreq.c index 37a480680cd07bb49b3aa439c6be95f158907fca..7906d4acfe4012da65bf25884cf89c69a86e91bc 100644 --- a/drivers/cpufreq/kirkwood-cpufreq.c +++ b/drivers/cpufreq/kirkwood-cpufreq.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -39,8 +38,7 @@ static struct priv * - cpu clk * - ddr clk * - * The frequencies are set at runtime before registering this * - * table. + * The frequencies are set at runtime before registering this table. */ static struct cpufreq_frequency_table kirkwood_freq_table[] = { {0, STATE_CPU_FREQ, 0}, /* CPU uses cpuclk */ @@ -50,9 +48,7 @@ static struct cpufreq_frequency_table kirkwood_freq_table[] = { static unsigned int kirkwood_cpufreq_get_cpu_frequency(unsigned int cpu) { - if (__clk_is_enabled(priv.powersave_clk)) - return kirkwood_freq_table[1].frequency; - return kirkwood_freq_table[0].frequency; + return clk_get_rate(priv.powersave_clk) / 1000; } static int kirkwood_cpufreq_target(struct cpufreq_policy *policy, @@ -70,10 +66,10 @@ static int kirkwood_cpufreq_target(struct cpufreq_policy *policy, switch (state) { case STATE_CPU_FREQ: - clk_disable(priv.powersave_clk); + clk_set_parent(priv.powersave_clk, priv.cpu_clk); break; case STATE_DDR_FREQ: - clk_enable(priv.powersave_clk); + clk_set_parent(priv.powersave_clk, priv.ddr_clk); break; } @@ -150,7 +146,7 @@ static int kirkwood_cpufreq_probe(struct platform_device *pdev) err = PTR_ERR(priv.powersave_clk); goto out_ddr; } - clk_prepare(priv.powersave_clk); + clk_prepare_enable(priv.powersave_clk); of_node_put(np); np = NULL; diff --git a/drivers/cpuidle/governors/ladder.c b/drivers/cpuidle/governors/ladder.c index 044ee0df5871949b8c1e0c0c744b4db068d4ba57..06b57c4c4d80debb885002bb48b7c318c5dbd5ec 100644 --- a/drivers/cpuidle/governors/ladder.c +++ b/drivers/cpuidle/governors/ladder.c @@ -66,7 +66,7 @@ static inline void ladder_do_selection(struct ladder_device *ldev, static int ladder_select_state(struct cpuidle_driver *drv, struct cpuidle_device *dev) { - struct ladder_device *ldev = &__get_cpu_var(ladder_devices); + struct ladder_device *ldev = this_cpu_ptr(&ladder_devices); struct ladder_device_state *last_state; int last_residency, last_idx = ldev->last_state_idx; int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); @@ -170,7 +170,7 @@ static int ladder_enable_device(struct cpuidle_driver *drv, */ static void ladder_reflect(struct cpuidle_device *dev, int index) { - struct ladder_device *ldev = &__get_cpu_var(ladder_devices); + struct ladder_device *ldev = this_cpu_ptr(&ladder_devices); if (index > 0) ldev->last_state_idx = index; } diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 34db2fb3ef1eb5ef17a2fb47ae5cd820e240c9e5..710a233b9b0d6e2fa0a3f06543283bde1cb885ee 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -289,7 +289,7 @@ static void get_typical_interval(struct menu_device *data) */ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) { - struct menu_device *data = &__get_cpu_var(menu_devices); + struct menu_device *data = this_cpu_ptr(&menu_devices); int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); int i; unsigned int interactivity_req; @@ -372,7 +372,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) */ static void menu_reflect(struct cpuidle_device *dev, int index) { - struct menu_device *data = &__get_cpu_var(menu_devices); + struct menu_device *data = this_cpu_ptr(&menu_devices); data->last_state_idx = index; if (index >= 0) data->needs_update = 1; @@ -385,7 +385,7 @@ static void menu_reflect(struct cpuidle_device *dev, int index) */ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) { - struct menu_device *data = &__get_cpu_var(menu_devices); + struct menu_device *data = this_cpu_ptr(&menu_devices); int last_idx = data->last_state_idx; struct cpuidle_state *target = &drv->states[last_idx]; unsigned int measured_us; diff --git a/drivers/crypto/ccp/ccp-crypto-sha.c b/drivers/crypto/ccp/ccp-crypto-sha.c index 873f234252456141911ddd5540e7f3270999edb9..96531571f7cf9216a584ac8421b62569201cf848 100644 --- a/drivers/crypto/ccp/ccp-crypto-sha.c +++ b/drivers/crypto/ccp/ccp-crypto-sha.c @@ -198,10 +198,9 @@ static int ccp_sha_setkey(struct crypto_ahash *tfm, const u8 *key, { struct ccp_ctx *ctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm)); struct crypto_shash *shash = ctx->u.sha.hmac_tfm; - struct { - struct shash_desc sdesc; - char ctx[crypto_shash_descsize(shash)]; - } desc; + + SHASH_DESC_ON_STACK(sdesc, shash); + unsigned int block_size = crypto_shash_blocksize(shash); unsigned int digest_size = crypto_shash_digestsize(shash); int i, ret; @@ -216,11 +215,11 @@ static int ccp_sha_setkey(struct crypto_ahash *tfm, const u8 *key, if (key_len > block_size) { /* Must hash the input key */ - desc.sdesc.tfm = shash; - desc.sdesc.flags = crypto_ahash_get_flags(tfm) & + sdesc->tfm = shash; + sdesc->flags = crypto_ahash_get_flags(tfm) & CRYPTO_TFM_REQ_MAY_SLEEP; - ret = crypto_shash_digest(&desc.sdesc, key, key_len, + ret = crypto_shash_digest(sdesc, key, key_len, ctx->u.sha.key); if (ret) { crypto_ahash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); diff --git a/drivers/crypto/mv_cesa.c b/drivers/crypto/mv_cesa.c index 29d0ee5049070c114696820c95d1b2daa3aef732..032c72c1f9539488011123901b1567a92b4731bd 100644 --- a/drivers/crypto/mv_cesa.c +++ b/drivers/crypto/mv_cesa.c @@ -402,26 +402,23 @@ static int mv_hash_final_fallback(struct ahash_request *req) { const struct mv_tfm_hash_ctx *tfm_ctx = crypto_tfm_ctx(req->base.tfm); struct mv_req_hash_ctx *req_ctx = ahash_request_ctx(req); - struct { - struct shash_desc shash; - char ctx[crypto_shash_descsize(tfm_ctx->fallback)]; - } desc; + SHASH_DESC_ON_STACK(shash, tfm_ctx->fallback); int rc; - desc.shash.tfm = tfm_ctx->fallback; - desc.shash.flags = CRYPTO_TFM_REQ_MAY_SLEEP; + shash->tfm = tfm_ctx->fallback; + shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP; if (unlikely(req_ctx->first_hash)) { - crypto_shash_init(&desc.shash); - crypto_shash_update(&desc.shash, req_ctx->buffer, + crypto_shash_init(shash); + crypto_shash_update(shash, req_ctx->buffer, req_ctx->extra_bytes); } else { /* only SHA1 for now.... */ - rc = mv_hash_import_sha1_ctx(req_ctx, &desc.shash); + rc = mv_hash_import_sha1_ctx(req_ctx, shash); if (rc) goto out; } - rc = crypto_shash_final(&desc.shash, req->result); + rc = crypto_shash_final(shash, req->result); out: return rc; } @@ -794,23 +791,21 @@ static int mv_hash_setkey(struct crypto_ahash *tfm, const u8 * key, ss = crypto_shash_statesize(ctx->base_hash); { - struct { - struct shash_desc shash; - char ctx[crypto_shash_descsize(ctx->base_hash)]; - } desc; + SHASH_DESC_ON_STACK(shash, ctx->base_hash); + unsigned int i; char ipad[ss]; char opad[ss]; - desc.shash.tfm = ctx->base_hash; - desc.shash.flags = crypto_shash_get_flags(ctx->base_hash) & + shash->tfm = ctx->base_hash; + shash->flags = crypto_shash_get_flags(ctx->base_hash) & CRYPTO_TFM_REQ_MAY_SLEEP; if (keylen > bs) { int err; err = - crypto_shash_digest(&desc.shash, key, keylen, ipad); + crypto_shash_digest(shash, key, keylen, ipad); if (err) return err; @@ -826,12 +821,12 @@ static int mv_hash_setkey(struct crypto_ahash *tfm, const u8 * key, opad[i] ^= 0x5c; } - rc = crypto_shash_init(&desc.shash) ? : - crypto_shash_update(&desc.shash, ipad, bs) ? : - crypto_shash_export(&desc.shash, ipad) ? : - crypto_shash_init(&desc.shash) ? : - crypto_shash_update(&desc.shash, opad, bs) ? : - crypto_shash_export(&desc.shash, opad); + rc = crypto_shash_init(shash) ? : + crypto_shash_update(shash, ipad, bs) ? : + crypto_shash_export(shash, ipad) ? : + crypto_shash_init(shash) ? : + crypto_shash_update(shash, opad, bs) ? : + crypto_shash_export(shash, opad); if (rc == 0) mv_hash_init_ivs(ctx, ipad, opad); diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c index 7263c10a56ee30914c1916418559901fa5090d7f..f8e3207fecb1c97b3c8fb6dca06b33e01d5f32d8 100644 --- a/drivers/crypto/n2_core.c +++ b/drivers/crypto/n2_core.c @@ -445,10 +445,7 @@ static int n2_hmac_async_setkey(struct crypto_ahash *tfm, const u8 *key, struct n2_hmac_ctx *ctx = crypto_ahash_ctx(tfm); struct crypto_shash *child_shash = ctx->child_shash; struct crypto_ahash *fallback_tfm; - struct { - struct shash_desc shash; - char ctx[crypto_shash_descsize(child_shash)]; - } desc; + SHASH_DESC_ON_STACK(shash, child_shash); int err, bs, ds; fallback_tfm = ctx->base.fallback_tfm; @@ -456,15 +453,15 @@ static int n2_hmac_async_setkey(struct crypto_ahash *tfm, const u8 *key, if (err) return err; - desc.shash.tfm = child_shash; - desc.shash.flags = crypto_ahash_get_flags(tfm) & + shash->tfm = child_shash; + shash->flags = crypto_ahash_get_flags(tfm) & CRYPTO_TFM_REQ_MAY_SLEEP; bs = crypto_shash_blocksize(child_shash); ds = crypto_shash_digestsize(child_shash); BUG_ON(ds > N2_HASH_KEY_MAX); if (keylen > bs) { - err = crypto_shash_digest(&desc.shash, key, keylen, + err = crypto_shash_digest(shash, key, keylen, ctx->hash_key); if (err) return err; diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 710d86386965a775ec06c3eb9e7ec788e502e8ea..24ef48965e45cc00d0319a579056e6259794da0b 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -949,17 +949,14 @@ static int omap_sham_finish_hmac(struct ahash_request *req) struct omap_sham_hmac_ctx *bctx = tctx->base; int bs = crypto_shash_blocksize(bctx->shash); int ds = crypto_shash_digestsize(bctx->shash); - struct { - struct shash_desc shash; - char ctx[crypto_shash_descsize(bctx->shash)]; - } desc; + SHASH_DESC_ON_STACK(shash, bctx->shash); - desc.shash.tfm = bctx->shash; - desc.shash.flags = 0; /* not CRYPTO_TFM_REQ_MAY_SLEEP */ + shash->tfm = bctx->shash; + shash->flags = 0; /* not CRYPTO_TFM_REQ_MAY_SLEEP */ - return crypto_shash_init(&desc.shash) ?: - crypto_shash_update(&desc.shash, bctx->opad, bs) ?: - crypto_shash_finup(&desc.shash, req->result, ds, req->result); + return crypto_shash_init(shash) ?: + crypto_shash_update(shash, bctx->opad, bs) ?: + crypto_shash_finup(shash, req->result, ds, req->result); } static int omap_sham_finish(struct ahash_request *req) @@ -1118,18 +1115,15 @@ static int omap_sham_update(struct ahash_request *req) return omap_sham_enqueue(req, OP_UPDATE); } -static int omap_sham_shash_digest(struct crypto_shash *shash, u32 flags, +static int omap_sham_shash_digest(struct crypto_shash *tfm, u32 flags, const u8 *data, unsigned int len, u8 *out) { - struct { - struct shash_desc shash; - char ctx[crypto_shash_descsize(shash)]; - } desc; + SHASH_DESC_ON_STACK(shash, tfm); - desc.shash.tfm = shash; - desc.shash.flags = flags & CRYPTO_TFM_REQ_MAY_SLEEP; + shash->tfm = tfm; + shash->flags = flags & CRYPTO_TFM_REQ_MAY_SLEEP; - return crypto_shash_digest(&desc.shash, data, len, out); + return crypto_shash_digest(shash, data, len, out); } static int omap_sham_final_shash(struct ahash_request *req) diff --git a/drivers/crypto/qat/qat_common/qat_algs.c b/drivers/crypto/qat/qat_common/qat_algs.c index 3e26fa2b293fa7bb4e8e8c3dff558990954bdd8b..f2e2f158cfbecec1bbef49a5b061d824c2365b3e 100644 --- a/drivers/crypto/qat/qat_common/qat_algs.c +++ b/drivers/crypto/qat/qat_common/qat_algs.c @@ -149,10 +149,7 @@ static int qat_alg_do_precomputes(struct icp_qat_hw_auth_algo_blk *hash, unsigned int auth_keylen) { struct qat_auth_state auth_state; - struct { - struct shash_desc shash; - char ctx[crypto_shash_descsize(ctx->hash_tfm)]; - } desc; + SHASH_DESC_ON_STACK(shash, ctx->hash_tfm); struct sha1_state sha1; struct sha256_state sha256; struct sha512_state sha512; @@ -165,12 +162,12 @@ static int qat_alg_do_precomputes(struct icp_qat_hw_auth_algo_blk *hash, int i, offset; memset(auth_state.data, '\0', MAX_AUTH_STATE_SIZE + 64); - desc.shash.tfm = ctx->hash_tfm; - desc.shash.flags = 0x0; + shash->tfm = ctx->hash_tfm; + shash->flags = 0x0; if (auth_keylen > block_size) { char buff[SHA512_BLOCK_SIZE]; - int ret = crypto_shash_digest(&desc.shash, auth_key, + int ret = crypto_shash_digest(shash, auth_key, auth_keylen, buff); if (ret) return ret; @@ -193,10 +190,10 @@ static int qat_alg_do_precomputes(struct icp_qat_hw_auth_algo_blk *hash, *opad_ptr ^= 0x5C; } - if (crypto_shash_init(&desc.shash)) + if (crypto_shash_init(shash)) return -EFAULT; - if (crypto_shash_update(&desc.shash, ipad, block_size)) + if (crypto_shash_update(shash, ipad, block_size)) return -EFAULT; hash_state_out = (__be32 *)hash->sha.state1; @@ -204,19 +201,19 @@ static int qat_alg_do_precomputes(struct icp_qat_hw_auth_algo_blk *hash, switch (ctx->qat_hash_alg) { case ICP_QAT_HW_AUTH_ALGO_SHA1: - if (crypto_shash_export(&desc.shash, &sha1)) + if (crypto_shash_export(shash, &sha1)) return -EFAULT; for (i = 0; i < digest_size >> 2; i++, hash_state_out++) *hash_state_out = cpu_to_be32(*(sha1.state + i)); break; case ICP_QAT_HW_AUTH_ALGO_SHA256: - if (crypto_shash_export(&desc.shash, &sha256)) + if (crypto_shash_export(shash, &sha256)) return -EFAULT; for (i = 0; i < digest_size >> 2; i++, hash_state_out++) *hash_state_out = cpu_to_be32(*(sha256.state + i)); break; case ICP_QAT_HW_AUTH_ALGO_SHA512: - if (crypto_shash_export(&desc.shash, &sha512)) + if (crypto_shash_export(shash, &sha512)) return -EFAULT; for (i = 0; i < digest_size >> 3; i++, hash512_state_out++) *hash512_state_out = cpu_to_be64(*(sha512.state + i)); @@ -225,10 +222,10 @@ static int qat_alg_do_precomputes(struct icp_qat_hw_auth_algo_blk *hash, return -EFAULT; } - if (crypto_shash_init(&desc.shash)) + if (crypto_shash_init(shash)) return -EFAULT; - if (crypto_shash_update(&desc.shash, opad, block_size)) + if (crypto_shash_update(shash, opad, block_size)) return -EFAULT; offset = round_up(qat_get_inter_state_size(ctx->qat_hash_alg), 8); @@ -237,19 +234,19 @@ static int qat_alg_do_precomputes(struct icp_qat_hw_auth_algo_blk *hash, switch (ctx->qat_hash_alg) { case ICP_QAT_HW_AUTH_ALGO_SHA1: - if (crypto_shash_export(&desc.shash, &sha1)) + if (crypto_shash_export(shash, &sha1)) return -EFAULT; for (i = 0; i < digest_size >> 2; i++, hash_state_out++) *hash_state_out = cpu_to_be32(*(sha1.state + i)); break; case ICP_QAT_HW_AUTH_ALGO_SHA256: - if (crypto_shash_export(&desc.shash, &sha256)) + if (crypto_shash_export(shash, &sha256)) return -EFAULT; for (i = 0; i < digest_size >> 2; i++, hash_state_out++) *hash_state_out = cpu_to_be32(*(sha256.state + i)); break; case ICP_QAT_HW_AUTH_ALGO_SHA512: - if (crypto_shash_export(&desc.shash, &sha512)) + if (crypto_shash_export(shash, &sha512)) return -EFAULT; for (i = 0; i < digest_size >> 3; i++, hash512_state_out++) *hash512_state_out = cpu_to_be64(*(sha512.state + i)); diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index a016490c95ae9c0a755302503bbe7f4e861631ef..de469821bc1b55e7e2afcc58521a31d41075c5b7 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -270,7 +270,7 @@ config IMX_SDMA select DMA_ENGINE help Support the i.MX SDMA engine. This engine is integrated into - Freescale i.MX25/31/35/51/53 chips. + Freescale i.MX25/31/35/51/53/6 chips. config IMX_DMA tristate "i.MX DMA support" diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index 3c6716e0b78eee2592a8c977550ce036ab4318c5..e88588d8ecd34b82dc23098ee73ea93a00e416fc 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -2156,7 +2156,7 @@ coh901318_free_chan_resources(struct dma_chan *chan) spin_unlock_irqrestore(&cohc->lock, flags); - chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(chan); } diff --git a/drivers/dma/cppi41.c b/drivers/dma/cppi41.c index 8f8b0b608875642e6ee60877a805f61a33e7d8bb..a58eec3b2cad0d0ac5af88a9900dd79d633e8e5a 100644 --- a/drivers/dma/cppi41.c +++ b/drivers/dma/cppi41.c @@ -938,7 +938,7 @@ static int cppi41_dma_probe(struct platform_device *pdev) if (!glue_info) return -EINVAL; - cdd = kzalloc(sizeof(*cdd), GFP_KERNEL); + cdd = devm_kzalloc(&pdev->dev, sizeof(*cdd), GFP_KERNEL); if (!cdd) return -ENOMEM; @@ -959,10 +959,8 @@ static int cppi41_dma_probe(struct platform_device *pdev) cdd->qmgr_mem = of_iomap(dev->of_node, 3); if (!cdd->usbss_mem || !cdd->ctrl_mem || !cdd->sched_mem || - !cdd->qmgr_mem) { - ret = -ENXIO; - goto err_remap; - } + !cdd->qmgr_mem) + return -ENXIO; pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); @@ -989,7 +987,7 @@ static int cppi41_dma_probe(struct platform_device *pdev) cppi_writel(USBSS_IRQ_PD_COMP, cdd->usbss_mem + USBSS_IRQ_ENABLER); - ret = request_irq(irq, glue_info->isr, IRQF_SHARED, + ret = devm_request_irq(&pdev->dev, irq, glue_info->isr, IRQF_SHARED, dev_name(dev), cdd); if (ret) goto err_irq; @@ -1009,7 +1007,6 @@ static int cppi41_dma_probe(struct platform_device *pdev) err_of: dma_async_device_unregister(&cdd->ddev); err_dma_reg: - free_irq(irq, cdd); err_irq: cppi_writel(0, cdd->usbss_mem + USBSS_IRQ_CLEARR); cleanup_chans(cdd); @@ -1023,8 +1020,6 @@ static int cppi41_dma_probe(struct platform_device *pdev) iounmap(cdd->ctrl_mem); iounmap(cdd->sched_mem); iounmap(cdd->qmgr_mem); -err_remap: - kfree(cdd); return ret; } @@ -1036,7 +1031,7 @@ static int cppi41_dma_remove(struct platform_device *pdev) dma_async_device_unregister(&cdd->ddev); cppi_writel(0, cdd->usbss_mem + USBSS_IRQ_CLEARR); - free_irq(cdd->irq, cdd); + devm_free_irq(&pdev->dev, cdd->irq, cdd); cleanup_chans(cdd); deinit_cppi41(&pdev->dev, cdd); iounmap(cdd->usbss_mem); @@ -1045,7 +1040,6 @@ static int cppi41_dma_remove(struct platform_device *pdev) iounmap(cdd->qmgr_mem); pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); - kfree(cdd); return 0; } diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index 1af731b83b3f56f284fd5655f85d90cda09bf2ca..244722170410fa774f1a1f5fd42898a26fe0cd9d 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -11,7 +11,6 @@ */ #include -#include #include #include #include @@ -37,24 +36,6 @@ * support descriptor writeback. */ -static inline bool is_request_line_unset(struct dw_dma_chan *dwc) -{ - return dwc->request_line == (typeof(dwc->request_line))~0; -} - -static inline void dwc_set_masters(struct dw_dma_chan *dwc) -{ - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - struct dw_dma_slave *dws = dwc->chan.private; - unsigned char mmax = dw->nr_masters - 1; - - if (!is_request_line_unset(dwc)) - return; - - dwc->src_master = min_t(unsigned char, mmax, dwc_get_sms(dws)); - dwc->dst_master = min_t(unsigned char, mmax, dwc_get_dms(dws)); -} - #define DWC_DEFAULT_CTLLO(_chan) ({ \ struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \ struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \ @@ -155,13 +136,11 @@ static void dwc_initialize(struct dw_dma_chan *dwc) */ BUG_ON(!dws->dma_dev || dws->dma_dev != dw->dma.dev); - cfghi = dws->cfg_hi; - cfglo |= dws->cfg_lo & ~DWC_CFGL_CH_PRIOR_MASK; + cfghi |= DWC_CFGH_DST_PER(dws->dst_id); + cfghi |= DWC_CFGH_SRC_PER(dws->src_id); } else { - if (dwc->direction == DMA_MEM_TO_DEV) - cfghi = DWC_CFGH_DST_PER(dwc->request_line); - else if (dwc->direction == DMA_DEV_TO_MEM) - cfghi = DWC_CFGH_SRC_PER(dwc->request_line); + cfghi |= DWC_CFGH_DST_PER(dwc->dst_id); + cfghi |= DWC_CFGH_SRC_PER(dwc->src_id); } channel_writel(dwc, CFG_LO, cfglo); @@ -939,6 +918,26 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, return NULL; } +bool dw_dma_filter(struct dma_chan *chan, void *param) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma_slave *dws = param; + + if (!dws || dws->dma_dev != chan->device->dev) + return false; + + /* We have to copy data since dws can be temporary storage */ + + dwc->src_id = dws->src_id; + dwc->dst_id = dws->dst_id; + + dwc->src_master = dws->src_master; + dwc->dst_master = dws->dst_master; + + return true; +} +EXPORT_SYMBOL_GPL(dw_dma_filter); + /* * Fix sconfig's burst size according to dw_dmac. We need to convert them as: * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3. @@ -967,10 +966,6 @@ set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); dwc->direction = sconfig->direction; - /* Take the request line from slave_id member */ - if (is_request_line_unset(dwc)) - dwc->request_line = sconfig->slave_id; - convert_burst(&dwc->dma_sconfig.src_maxburst); convert_burst(&dwc->dma_sconfig.dst_maxburst); @@ -1099,6 +1094,31 @@ static void dwc_issue_pending(struct dma_chan *chan) spin_unlock_irqrestore(&dwc->lock, flags); } +/*----------------------------------------------------------------------*/ + +static void dw_dma_off(struct dw_dma *dw) +{ + int i; + + dma_writel(dw, CFG, 0); + + channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_clear_bit(dw, MASK.SRC_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.DST_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); + + while (dma_readl(dw, CFG) & DW_CFG_DMA_EN) + cpu_relax(); + + for (i = 0; i < dw->dma.chancnt; i++) + dw->chan[i].initialized = false; +} + +static void dw_dma_on(struct dw_dma *dw) +{ + dma_writel(dw, CFG, DW_CFG_DMA_EN); +} + static int dwc_alloc_chan_resources(struct dma_chan *chan) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); @@ -1123,7 +1143,10 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) * doesn't mean what you think it means), and status writeback. */ - dwc_set_masters(dwc); + /* Enable controller here if needed */ + if (!dw->in_use) + dw_dma_on(dw); + dw->in_use |= dwc->mask; spin_lock_irqsave(&dwc->lock, flags); i = dwc->descs_allocated; @@ -1182,7 +1205,6 @@ static void dwc_free_chan_resources(struct dma_chan *chan) list_splice_init(&dwc->free_list, &list); dwc->descs_allocated = 0; dwc->initialized = false; - dwc->request_line = ~0; /* Disable interrupts */ channel_clear_bit(dw, MASK.XFER, dwc->mask); @@ -1190,6 +1212,11 @@ static void dwc_free_chan_resources(struct dma_chan *chan) spin_unlock_irqrestore(&dwc->lock, flags); + /* Disable controller in case it was a last user */ + dw->in_use &= ~dwc->mask; + if (!dw->in_use) + dw_dma_off(dw); + list_for_each_entry_safe(desc, _desc, &list, desc_node) { dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc); dma_pool_free(dw->desc_pool, desc, desc->txd.phys); @@ -1460,24 +1487,6 @@ EXPORT_SYMBOL(dw_dma_cyclic_free); /*----------------------------------------------------------------------*/ -static void dw_dma_off(struct dw_dma *dw) -{ - int i; - - dma_writel(dw, CFG, 0); - - channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); - channel_clear_bit(dw, MASK.SRC_TRAN, dw->all_chan_mask); - channel_clear_bit(dw, MASK.DST_TRAN, dw->all_chan_mask); - channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); - - while (dma_readl(dw, CFG) & DW_CFG_DMA_EN) - cpu_relax(); - - for (i = 0; i < dw->dma.chancnt; i++) - dw->chan[i].initialized = false; -} - int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) { struct dw_dma *dw; @@ -1495,13 +1504,6 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) dw->regs = chip->regs; chip->dw = dw; - dw->clk = devm_clk_get(chip->dev, "hclk"); - if (IS_ERR(dw->clk)) - return PTR_ERR(dw->clk); - err = clk_prepare_enable(dw->clk); - if (err) - return err; - dw_params = dma_read_byaddr(chip->regs, DW_PARAMS); autocfg = dw_params >> DW_PARAMS_EN & 0x1; @@ -1604,7 +1606,6 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) channel_clear_bit(dw, CH_EN, dwc->mask); dwc->direction = DMA_TRANS_NONE; - dwc->request_line = ~0; /* Hardware configuration */ if (autocfg) { @@ -1659,8 +1660,6 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) dw->dma.device_tx_status = dwc_tx_status; dw->dma.device_issue_pending = dwc_issue_pending; - dma_writel(dw, CFG, DW_CFG_DMA_EN); - err = dma_async_device_register(&dw->dma); if (err) goto err_dma_register; @@ -1673,7 +1672,6 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) err_dma_register: free_irq(chip->irq, dw); err_pdata: - clk_disable_unprepare(dw->clk); return err; } EXPORT_SYMBOL_GPL(dw_dma_probe); @@ -1695,46 +1693,27 @@ int dw_dma_remove(struct dw_dma_chip *chip) channel_clear_bit(dw, CH_EN, dwc->mask); } - clk_disable_unprepare(dw->clk); - return 0; } EXPORT_SYMBOL_GPL(dw_dma_remove); -void dw_dma_shutdown(struct dw_dma_chip *chip) -{ - struct dw_dma *dw = chip->dw; - - dw_dma_off(dw); - clk_disable_unprepare(dw->clk); -} -EXPORT_SYMBOL_GPL(dw_dma_shutdown); - -#ifdef CONFIG_PM_SLEEP - -int dw_dma_suspend(struct dw_dma_chip *chip) +int dw_dma_disable(struct dw_dma_chip *chip) { struct dw_dma *dw = chip->dw; dw_dma_off(dw); - clk_disable_unprepare(dw->clk); - return 0; } -EXPORT_SYMBOL_GPL(dw_dma_suspend); +EXPORT_SYMBOL_GPL(dw_dma_disable); -int dw_dma_resume(struct dw_dma_chip *chip) +int dw_dma_enable(struct dw_dma_chip *chip) { struct dw_dma *dw = chip->dw; - clk_prepare_enable(dw->clk); - dma_writel(dw, CFG, DW_CFG_DMA_EN); - + dw_dma_on(dw); return 0; } -EXPORT_SYMBOL_GPL(dw_dma_resume); - -#endif /* CONFIG_PM_SLEEP */ +EXPORT_SYMBOL_GPL(dw_dma_enable); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller core driver"); diff --git a/drivers/dma/dw/internal.h b/drivers/dma/dw/internal.h index 32667f9e0ddab2503fe89522e0d987cf65ad5b44..41439732ff6b8ae9b09822330de2921bf64ee37b 100644 --- a/drivers/dma/dw/internal.h +++ b/drivers/dma/dw/internal.h @@ -8,63 +8,16 @@ * published by the Free Software Foundation. */ -#ifndef _DW_DMAC_INTERNAL_H -#define _DW_DMAC_INTERNAL_H +#ifndef _DMA_DW_INTERNAL_H +#define _DMA_DW_INTERNAL_H -#include -#include +#include #include "regs.h" -/** - * struct dw_dma_chip - representation of DesignWare DMA controller hardware - * @dev: struct device of the DMA controller - * @irq: irq line - * @regs: memory mapped I/O space - * @dw: struct dw_dma that is filed by dw_dma_probe() - */ -struct dw_dma_chip { - struct device *dev; - int irq; - void __iomem *regs; - struct dw_dma *dw; -}; - -/* Export to the platform drivers */ -int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata); -int dw_dma_remove(struct dw_dma_chip *chip); - -void dw_dma_shutdown(struct dw_dma_chip *chip); - -#ifdef CONFIG_PM_SLEEP - -int dw_dma_suspend(struct dw_dma_chip *chip); -int dw_dma_resume(struct dw_dma_chip *chip); - -#endif /* CONFIG_PM_SLEEP */ +int dw_dma_disable(struct dw_dma_chip *chip); +int dw_dma_enable(struct dw_dma_chip *chip); -/** - * dwc_get_dms - get destination master - * @slave: pointer to the custom slave configuration - * - * Returns destination master in the custom slave configuration if defined, or - * default value otherwise. - */ -static inline unsigned int dwc_get_dms(struct dw_dma_slave *slave) -{ - return slave ? slave->dst_master : 0; -} - -/** - * dwc_get_sms - get source master - * @slave: pointer to the custom slave configuration - * - * Returns source master in the custom slave configuration if defined, or - * default value otherwise. - */ -static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) -{ - return slave ? slave->src_master : 1; -} +extern bool dw_dma_filter(struct dma_chan *chan, void *param); -#endif /* _DW_DMAC_INTERNAL_H */ +#endif /* _DMA_DW_INTERNAL_H */ diff --git a/drivers/dma/dw/pci.c b/drivers/dma/dw/pci.c index 39e30c3c7a9d4e810ee91151f3c878d093dcf6c9..b144706b3d857443945f58a2d0d3fb23a98e74cf 100644 --- a/drivers/dma/dw/pci.c +++ b/drivers/dma/dw/pci.c @@ -82,7 +82,7 @@ static int dw_pci_suspend_late(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct dw_dma_chip *chip = pci_get_drvdata(pci); - return dw_dma_suspend(chip); + return dw_dma_disable(chip); }; static int dw_pci_resume_early(struct device *dev) @@ -90,7 +90,7 @@ static int dw_pci_resume_early(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct dw_dma_chip *chip = pci_get_drvdata(pci); - return dw_dma_resume(chip); + return dw_dma_enable(chip); }; #endif /* CONFIG_PM_SLEEP */ @@ -108,6 +108,10 @@ static const struct pci_device_id dw_pci_id_table[] = { { PCI_VDEVICE(INTEL, 0x0f06), (kernel_ulong_t)&dw_pci_pdata }, { PCI_VDEVICE(INTEL, 0x0f40), (kernel_ulong_t)&dw_pci_pdata }, + /* Braswell */ + { PCI_VDEVICE(INTEL, 0x2286), (kernel_ulong_t)&dw_pci_pdata }, + { PCI_VDEVICE(INTEL, 0x22c0), (kernel_ulong_t)&dw_pci_pdata }, + /* Haswell */ { PCI_VDEVICE(INTEL, 0x9c60), (kernel_ulong_t)&dw_pci_pdata }, { } diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c index c5b339af6be572a7255ad1a3dbadaa3925579930..a630161473a4fa69c2586a257949d7f0956d4db7 100644 --- a/drivers/dma/dw/platform.c +++ b/drivers/dma/dw/platform.c @@ -25,72 +25,49 @@ #include "internal.h" -struct dw_dma_of_filter_args { - struct dw_dma *dw; - unsigned int req; - unsigned int src; - unsigned int dst; -}; - -static bool dw_dma_of_filter(struct dma_chan *chan, void *param) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma_of_filter_args *fargs = param; - - /* Ensure the device matches our channel */ - if (chan->device != &fargs->dw->dma) - return false; - - dwc->request_line = fargs->req; - dwc->src_master = fargs->src; - dwc->dst_master = fargs->dst; - - return true; -} - static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) { struct dw_dma *dw = ofdma->of_dma_data; - struct dw_dma_of_filter_args fargs = { - .dw = dw, + struct dw_dma_slave slave = { + .dma_dev = dw->dma.dev, }; dma_cap_mask_t cap; if (dma_spec->args_count != 3) return NULL; - fargs.req = dma_spec->args[0]; - fargs.src = dma_spec->args[1]; - fargs.dst = dma_spec->args[2]; + slave.src_id = dma_spec->args[0]; + slave.dst_id = dma_spec->args[0]; + slave.src_master = dma_spec->args[1]; + slave.dst_master = dma_spec->args[2]; - if (WARN_ON(fargs.req >= DW_DMA_MAX_NR_REQUESTS || - fargs.src >= dw->nr_masters || - fargs.dst >= dw->nr_masters)) + if (WARN_ON(slave.src_id >= DW_DMA_MAX_NR_REQUESTS || + slave.dst_id >= DW_DMA_MAX_NR_REQUESTS || + slave.src_master >= dw->nr_masters || + slave.dst_master >= dw->nr_masters)) return NULL; dma_cap_zero(cap); dma_cap_set(DMA_SLAVE, cap); /* TODO: there should be a simpler way to do this */ - return dma_request_channel(cap, dw_dma_of_filter, &fargs); + return dma_request_channel(cap, dw_dma_filter, &slave); } #ifdef CONFIG_ACPI static bool dw_dma_acpi_filter(struct dma_chan *chan, void *param) { - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); struct acpi_dma_spec *dma_spec = param; + struct dw_dma_slave slave = { + .dma_dev = dma_spec->dev, + .src_id = dma_spec->slave_id, + .dst_id = dma_spec->slave_id, + .src_master = 1, + .dst_master = 0, + }; - if (chan->device->dev != dma_spec->dev || - chan->chan_id != dma_spec->chan_id) - return false; - - dwc->request_line = dma_spec->slave_id; - dwc->src_master = dwc_get_sms(NULL); - dwc->dst_master = dwc_get_dms(NULL); - - return true; + return dw_dma_filter(chan, &slave); } static void dw_dma_acpi_controller_register(struct dw_dma *dw) @@ -201,10 +178,17 @@ static int dw_probe(struct platform_device *pdev) chip->dev = dev; - err = dw_dma_probe(chip, pdata); + chip->clk = devm_clk_get(chip->dev, "hclk"); + if (IS_ERR(chip->clk)) + return PTR_ERR(chip->clk); + err = clk_prepare_enable(chip->clk); if (err) return err; + err = dw_dma_probe(chip, pdata); + if (err) + goto err_dw_dma_probe; + platform_set_drvdata(pdev, chip); if (pdev->dev.of_node) { @@ -219,6 +203,10 @@ static int dw_probe(struct platform_device *pdev) dw_dma_acpi_controller_register(chip->dw); return 0; + +err_dw_dma_probe: + clk_disable_unprepare(chip->clk); + return err; } static int dw_remove(struct platform_device *pdev) @@ -228,14 +216,18 @@ static int dw_remove(struct platform_device *pdev) if (pdev->dev.of_node) of_dma_controller_free(pdev->dev.of_node); - return dw_dma_remove(chip); + dw_dma_remove(chip); + clk_disable_unprepare(chip->clk); + + return 0; } static void dw_shutdown(struct platform_device *pdev) { struct dw_dma_chip *chip = platform_get_drvdata(pdev); - dw_dma_shutdown(chip); + dw_dma_disable(chip); + clk_disable_unprepare(chip->clk); } #ifdef CONFIG_OF @@ -261,7 +253,10 @@ static int dw_suspend_late(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct dw_dma_chip *chip = platform_get_drvdata(pdev); - return dw_dma_suspend(chip); + dw_dma_disable(chip); + clk_disable_unprepare(chip->clk); + + return 0; } static int dw_resume_early(struct device *dev) @@ -269,7 +264,8 @@ static int dw_resume_early(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct dw_dma_chip *chip = platform_get_drvdata(pdev); - return dw_dma_resume(chip); + clk_prepare_enable(chip->clk); + return dw_dma_enable(chip); } #endif /* CONFIG_PM_SLEEP */ @@ -281,7 +277,7 @@ static const struct dev_pm_ops dw_dev_pm_ops = { static struct platform_driver dw_driver = { .probe = dw_probe, .remove = dw_remove, - .shutdown = dw_shutdown, + .shutdown = dw_shutdown, .driver = { .name = "dw_dmac", .pm = &dw_dev_pm_ops, diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h index bb98d3e91e8b2a6a656ba58bc31739c620e4c75a..848e232f7cc755991eda6e4022771d7cba7e1c20 100644 --- a/drivers/dma/dw/regs.h +++ b/drivers/dma/dw/regs.h @@ -11,7 +11,6 @@ #include #include -#include #define DW_DMA_MAX_NR_CHANNELS 8 #define DW_DMA_MAX_NR_REQUESTS 16 @@ -132,6 +131,18 @@ struct dw_dma_regs { /* Bitfields in DWC_PARAMS */ #define DWC_PARAMS_MBLK_EN 11 /* multi block transfer */ +/* bursts size */ +enum dw_dma_msize { + DW_DMA_MSIZE_1, + DW_DMA_MSIZE_4, + DW_DMA_MSIZE_8, + DW_DMA_MSIZE_16, + DW_DMA_MSIZE_32, + DW_DMA_MSIZE_64, + DW_DMA_MSIZE_128, + DW_DMA_MSIZE_256, +}; + /* Bitfields in CTL_LO */ #define DWC_CTLL_INT_EN (1 << 0) /* irqs enabled? */ #define DWC_CTLL_DST_WIDTH(n) ((n)<<1) /* bytes per element */ @@ -161,20 +172,35 @@ struct dw_dma_regs { #define DWC_CTLH_DONE 0x00001000 #define DWC_CTLH_BLOCK_TS_MASK 0x00000fff -/* Bitfields in CFG_LO. Platform-configurable bits are in */ +/* Bitfields in CFG_LO */ #define DWC_CFGL_CH_PRIOR_MASK (0x7 << 5) /* priority mask */ #define DWC_CFGL_CH_PRIOR(x) ((x) << 5) /* priority */ #define DWC_CFGL_CH_SUSP (1 << 8) /* pause xfer */ #define DWC_CFGL_FIFO_EMPTY (1 << 9) /* pause xfer */ #define DWC_CFGL_HS_DST (1 << 10) /* handshake w/dst */ #define DWC_CFGL_HS_SRC (1 << 11) /* handshake w/src */ +#define DWC_CFGL_LOCK_CH_XFER (0 << 12) /* scope of LOCK_CH */ +#define DWC_CFGL_LOCK_CH_BLOCK (1 << 12) +#define DWC_CFGL_LOCK_CH_XACT (2 << 12) +#define DWC_CFGL_LOCK_BUS_XFER (0 << 14) /* scope of LOCK_BUS */ +#define DWC_CFGL_LOCK_BUS_BLOCK (1 << 14) +#define DWC_CFGL_LOCK_BUS_XACT (2 << 14) +#define DWC_CFGL_LOCK_CH (1 << 15) /* channel lockout */ +#define DWC_CFGL_LOCK_BUS (1 << 16) /* busmaster lockout */ +#define DWC_CFGL_HS_DST_POL (1 << 18) /* dst handshake active low */ +#define DWC_CFGL_HS_SRC_POL (1 << 19) /* src handshake active low */ #define DWC_CFGL_MAX_BURST(x) ((x) << 20) #define DWC_CFGL_RELOAD_SAR (1 << 30) #define DWC_CFGL_RELOAD_DAR (1 << 31) -/* Bitfields in CFG_HI. Platform-configurable bits are in */ +/* Bitfields in CFG_HI */ +#define DWC_CFGH_FCMODE (1 << 0) +#define DWC_CFGH_FIFO_MODE (1 << 1) +#define DWC_CFGH_PROTCTL(x) ((x) << 2) #define DWC_CFGH_DS_UPD_EN (1 << 5) #define DWC_CFGH_SS_UPD_EN (1 << 6) +#define DWC_CFGH_SRC_PER(x) ((x) << 7) +#define DWC_CFGH_DST_PER(x) ((x) << 11) /* Bitfields in SGR */ #define DWC_SGR_SGI(x) ((x) << 0) @@ -221,9 +247,10 @@ struct dw_dma_chan { bool nollp; /* custom slave configuration */ - unsigned int request_line; - unsigned char src_master; - unsigned char dst_master; + u8 src_id; + u8 dst_id; + u8 src_master; + u8 dst_master; /* configuration passed via DMA_SLAVE_CONFIG */ struct dma_slave_config dma_sconfig; @@ -250,11 +277,11 @@ struct dw_dma { void __iomem *regs; struct dma_pool *desc_pool; struct tasklet_struct tasklet; - struct clk *clk; /* channels */ struct dw_dma_chan *chan; u8 all_chan_mask; + u8 in_use; /* hardware configuration */ unsigned char nr_masters; diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 7b65633f495ed28ca77842231ea65a01c8870f4c..123f578d6dd3b82d37887182fc092937c40e59d4 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -288,7 +288,7 @@ static int edma_slave_config(struct edma_chan *echan, static int edma_dma_pause(struct edma_chan *echan) { /* Pause/Resume only allowed with cyclic mode */ - if (!echan->edesc->cyclic) + if (!echan->edesc || !echan->edesc->cyclic) return -EINVAL; edma_pause(echan->ch_num); diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index d5d6885ab341bc24119e056733be1443ba91a614..994bcb2c6b92ceb44edcb643300e879003455db8 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -36,7 +36,7 @@ #include #include #include - +#include #include "dmaengine.h" #include "fsldma.h" @@ -367,6 +367,20 @@ static void fsl_chan_toggle_ext_start(struct fsldma_chan *chan, int enable) chan->feature &= ~FSL_DMA_CHAN_START_EXT; } +int fsl_dma_external_start(struct dma_chan *dchan, int enable) +{ + struct fsldma_chan *chan; + + if (!dchan) + return -EINVAL; + + chan = to_fsl_chan(dchan); + + fsl_chan_toggle_ext_start(chan, enable); + return 0; +} +EXPORT_SYMBOL_GPL(fsl_dma_external_start); + static void append_ld_queue(struct fsldma_chan *chan, struct fsl_desc_sw *desc) { struct fsl_desc_sw *tail = to_fsl_desc(chan->ld_pending.prev); @@ -998,15 +1012,6 @@ static int fsl_dma_device_control(struct dma_chan *dchan, chan->set_request_count(chan, size); return 0; - case FSLDMA_EXTERNAL_START: - - /* make sure the channel supports external start */ - if (!chan->toggle_ext_start) - return -ENXIO; - - chan->toggle_ext_start(chan, arg); - return 0; - default: return -ENXIO; } diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index f7626e37d0b824ec077f75e0a2d323060c38052b..88afc48c2ca718e62b567f7d48ac8fe27fae7c58 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1334,7 +1334,7 @@ static void sdma_load_firmware(const struct firmware *fw, void *context) release_firmware(fw); } -static int __init sdma_get_firmware(struct sdma_engine *sdma, +static int sdma_get_firmware(struct sdma_engine *sdma, const char *fw_name) { int ret; @@ -1448,7 +1448,7 @@ static struct dma_chan *sdma_xlate(struct of_phandle_args *dma_spec, return dma_request_channel(mask, sdma_filter_fn, &data); } -static int __init sdma_probe(struct platform_device *pdev) +static int sdma_probe(struct platform_device *pdev) { const struct of_device_id *of_id = of_match_device(sdma_dt_ids, &pdev->dev); @@ -1603,6 +1603,8 @@ static int __init sdma_probe(struct platform_device *pdev) sdma->dma_device.dev->dma_parms = &sdma->dma_parms; dma_set_max_seg_size(sdma->dma_device.dev, 65535); + platform_set_drvdata(pdev, sdma); + ret = dma_async_device_register(&sdma->dma_device); if (ret) { dev_err(&pdev->dev, "unable to register\n"); @@ -1640,7 +1642,27 @@ static int __init sdma_probe(struct platform_device *pdev) static int sdma_remove(struct platform_device *pdev) { - return -EBUSY; + struct sdma_engine *sdma = platform_get_drvdata(pdev); + struct resource *iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int irq = platform_get_irq(pdev, 0); + int i; + + dma_async_device_unregister(&sdma->dma_device); + kfree(sdma->script_addrs); + free_irq(irq, sdma); + iounmap(sdma->regs); + release_mem_region(iores->start, resource_size(iores)); + /* Kill the tasklet */ + for (i = 0; i < MAX_DMA_CHANNELS; i++) { + struct sdma_channel *sdmac = &sdma->channel[i]; + + tasklet_kill(&sdmac->tasklet); + } + kfree(sdma); + + platform_set_drvdata(pdev, NULL); + dev_info(&pdev->dev, "Removed...\n"); + return 0; } static struct platform_driver sdma_driver = { @@ -1650,13 +1672,10 @@ static struct platform_driver sdma_driver = { }, .id_table = sdma_devtypes, .remove = sdma_remove, + .probe = sdma_probe, }; -static int __init sdma_module_init(void) -{ - return platform_driver_probe(&sdma_driver, sdma_probe); -} -module_init(sdma_module_init); +module_platform_driver(sdma_driver); MODULE_AUTHOR("Sascha Hauer, Pengutronix "); MODULE_DESCRIPTION("i.MX SDMA driver"); diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c index 6ad30e2c5038351ed80e6abc8b965c9b1efa38f5..c6bd015b7165a0d9b808ed6a6277b1c48b654425 100644 --- a/drivers/dma/mmp_tdma.c +++ b/drivers/dma/mmp_tdma.c @@ -148,10 +148,16 @@ static void mmp_tdma_chan_set_desc(struct mmp_tdma_chan *tdmac, dma_addr_t phys) tdmac->reg_base + TDCR); } +static void mmp_tdma_enable_irq(struct mmp_tdma_chan *tdmac, bool enable) +{ + if (enable) + writel(TDIMR_COMP, tdmac->reg_base + TDIMR); + else + writel(0, tdmac->reg_base + TDIMR); +} + static void mmp_tdma_enable_chan(struct mmp_tdma_chan *tdmac) { - /* enable irq */ - writel(TDIMR_COMP, tdmac->reg_base + TDIMR); /* enable dma chan */ writel(readl(tdmac->reg_base + TDCR) | TDCR_CHANEN, tdmac->reg_base + TDCR); @@ -163,9 +169,6 @@ static void mmp_tdma_disable_chan(struct mmp_tdma_chan *tdmac) writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CHANEN, tdmac->reg_base + TDCR); - /* disable irq */ - writel(0, tdmac->reg_base + TDIMR); - tdmac->status = DMA_COMPLETE; } @@ -434,6 +437,10 @@ static struct dma_async_tx_descriptor *mmp_tdma_prep_dma_cyclic( i++; } + /* enable interrupt */ + if (flags & DMA_PREP_INTERRUPT) + mmp_tdma_enable_irq(tdmac, true); + tdmac->buf_len = buf_len; tdmac->period_len = period_len; tdmac->pos = 0; @@ -455,6 +462,8 @@ static int mmp_tdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, switch (cmd) { case DMA_TERMINATE_ALL: mmp_tdma_disable_chan(tdmac); + /* disable interrupt */ + mmp_tdma_enable_irq(tdmac, false); break; case DMA_PAUSE: mmp_tdma_pause_chan(tdmac); diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index 7938272f2edf4faf63518874153298a707e03098..a63837ca14106a9831824af4e2e11a0c5ae44188 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -45,19 +45,18 @@ static void mv_xor_issue_pending(struct dma_chan *chan); #define mv_chan_to_devp(chan) \ ((chan)->dmadev.dev) -static void mv_desc_init(struct mv_xor_desc_slot *desc, unsigned long flags) +static void mv_desc_init(struct mv_xor_desc_slot *desc, + dma_addr_t addr, u32 byte_count, + enum dma_ctrl_flags flags) { struct mv_xor_desc *hw_desc = desc->hw_desc; - hw_desc->status = (1 << 31); + hw_desc->status = XOR_DESC_DMA_OWNED; hw_desc->phy_next_desc = 0; - hw_desc->desc_command = (1 << 31); -} - -static void mv_desc_set_byte_count(struct mv_xor_desc_slot *desc, - u32 byte_count) -{ - struct mv_xor_desc *hw_desc = desc->hw_desc; + /* Enable end-of-descriptor interrupts only for DMA_PREP_INTERRUPT */ + hw_desc->desc_command = (flags & DMA_PREP_INTERRUPT) ? + XOR_DESC_EOD_INT_EN : 0; + hw_desc->phy_dest_addr = addr; hw_desc->byte_count = byte_count; } @@ -75,20 +74,6 @@ static void mv_desc_clear_next_desc(struct mv_xor_desc_slot *desc) hw_desc->phy_next_desc = 0; } -static void mv_desc_set_dest_addr(struct mv_xor_desc_slot *desc, - dma_addr_t addr) -{ - struct mv_xor_desc *hw_desc = desc->hw_desc; - hw_desc->phy_dest_addr = addr; -} - -static int mv_chan_memset_slot_count(size_t len) -{ - return 1; -} - -#define mv_chan_memcpy_slot_count(c) mv_chan_memset_slot_count(c) - static void mv_desc_set_src_addr(struct mv_xor_desc_slot *desc, int index, dma_addr_t addr) { @@ -123,17 +108,12 @@ static u32 mv_chan_get_intr_cause(struct mv_xor_chan *chan) return intr_cause; } -static int mv_is_err_intr(u32 intr_cause) -{ - if (intr_cause & ((1<<4)|(1<<5)|(1<<6)|(1<<7)|(1<<8)|(1<<9))) - return 1; - - return 0; -} - static void mv_xor_device_clear_eoc_cause(struct mv_xor_chan *chan) { - u32 val = ~(1 << (chan->idx * 16)); + u32 val; + + val = XOR_INT_END_OF_DESC | XOR_INT_END_OF_CHAIN | XOR_INT_STOPPED; + val = ~(val << (chan->idx * 16)); dev_dbg(mv_chan_to_devp(chan), "%s, val 0x%08x\n", __func__, val); writel_relaxed(val, XOR_INTR_CAUSE(chan)); } @@ -144,17 +124,6 @@ static void mv_xor_device_clear_err_status(struct mv_xor_chan *chan) writel_relaxed(val, XOR_INTR_CAUSE(chan)); } -static int mv_can_chain(struct mv_xor_desc_slot *desc) -{ - struct mv_xor_desc_slot *chain_old_tail = list_entry( - desc->chain_node.prev, struct mv_xor_desc_slot, chain_node); - - if (chain_old_tail->type != desc->type) - return 0; - - return 1; -} - static void mv_set_mode(struct mv_xor_chan *chan, enum dma_transaction_type type) { @@ -206,11 +175,6 @@ static char mv_chan_is_busy(struct mv_xor_chan *chan) return (state == 1) ? 1 : 0; } -static int mv_chan_xor_slot_count(size_t len, int src_cnt) -{ - return 1; -} - /** * mv_xor_free_slots - flags descriptor slots for reuse * @slot: Slot to free @@ -222,7 +186,7 @@ static void mv_xor_free_slots(struct mv_xor_chan *mv_chan, dev_dbg(mv_chan_to_devp(mv_chan), "%s %d slot %p\n", __func__, __LINE__, slot); - slot->slots_per_op = 0; + slot->slot_used = 0; } @@ -236,13 +200,11 @@ static void mv_xor_start_new_chain(struct mv_xor_chan *mv_chan, { dev_dbg(mv_chan_to_devp(mv_chan), "%s %d: sw_desc %p\n", __func__, __LINE__, sw_desc); - if (sw_desc->type != mv_chan->current_type) - mv_set_mode(mv_chan, sw_desc->type); /* set the hardware chain */ mv_chan_set_next_descriptor(mv_chan, sw_desc->async_tx.phys); - mv_chan->pending += sw_desc->slot_cnt; + mv_chan->pending++; mv_xor_issue_pending(&mv_chan->dmachan); } @@ -263,8 +225,6 @@ mv_xor_run_tx_complete_actions(struct mv_xor_desc_slot *desc, desc->async_tx.callback_param); dma_descriptor_unmap(&desc->async_tx); - if (desc->group_head) - desc->group_head = NULL; } /* run dependent operations */ @@ -377,19 +337,16 @@ static void mv_xor_tasklet(unsigned long data) } static struct mv_xor_desc_slot * -mv_xor_alloc_slots(struct mv_xor_chan *mv_chan, int num_slots, - int slots_per_op) +mv_xor_alloc_slot(struct mv_xor_chan *mv_chan) { - struct mv_xor_desc_slot *iter, *_iter, *alloc_start = NULL; - LIST_HEAD(chain); - int slots_found, retry = 0; + struct mv_xor_desc_slot *iter, *_iter; + int retry = 0; /* start search from the last allocated descrtiptor * if a contiguous allocation can not be found start searching * from the beginning of the list */ retry: - slots_found = 0; if (retry == 0) iter = mv_chan->last_used; else @@ -399,55 +356,29 @@ mv_xor_alloc_slots(struct mv_xor_chan *mv_chan, int num_slots, list_for_each_entry_safe_continue( iter, _iter, &mv_chan->all_slots, slot_node) { + prefetch(_iter); prefetch(&_iter->async_tx); - if (iter->slots_per_op) { + if (iter->slot_used) { /* give up after finding the first busy slot * on the second pass through the list */ if (retry) break; - - slots_found = 0; continue; } - /* start the allocation if the slot is correctly aligned */ - if (!slots_found++) - alloc_start = iter; - - if (slots_found == num_slots) { - struct mv_xor_desc_slot *alloc_tail = NULL; - struct mv_xor_desc_slot *last_used = NULL; - iter = alloc_start; - while (num_slots) { - int i; - - /* pre-ack all but the last descriptor */ - async_tx_ack(&iter->async_tx); - - list_add_tail(&iter->chain_node, &chain); - alloc_tail = iter; - iter->async_tx.cookie = 0; - iter->slot_cnt = num_slots; - iter->xor_check_result = NULL; - for (i = 0; i < slots_per_op; i++) { - iter->slots_per_op = slots_per_op - i; - last_used = iter; - iter = list_entry(iter->slot_node.next, - struct mv_xor_desc_slot, - slot_node); - } - num_slots -= slots_per_op; - } - alloc_tail->group_head = alloc_start; - alloc_tail->async_tx.cookie = -EBUSY; - list_splice(&chain, &alloc_tail->tx_list); - mv_chan->last_used = last_used; - mv_desc_clear_next_desc(alloc_start); - mv_desc_clear_next_desc(alloc_tail); - return alloc_tail; - } + /* pre-ack descriptor */ + async_tx_ack(&iter->async_tx); + + iter->slot_used = 1; + INIT_LIST_HEAD(&iter->chain_node); + iter->async_tx.cookie = -EBUSY; + mv_chan->last_used = iter; + mv_desc_clear_next_desc(iter); + + return iter; + } if (!retry++) goto retry; @@ -464,7 +395,7 @@ mv_xor_tx_submit(struct dma_async_tx_descriptor *tx) { struct mv_xor_desc_slot *sw_desc = to_mv_xor_slot(tx); struct mv_xor_chan *mv_chan = to_mv_xor_chan(tx->chan); - struct mv_xor_desc_slot *grp_start, *old_chain_tail; + struct mv_xor_desc_slot *old_chain_tail; dma_cookie_t cookie; int new_hw_chain = 1; @@ -472,30 +403,24 @@ mv_xor_tx_submit(struct dma_async_tx_descriptor *tx) "%s sw_desc %p: async_tx %p\n", __func__, sw_desc, &sw_desc->async_tx); - grp_start = sw_desc->group_head; - spin_lock_bh(&mv_chan->lock); cookie = dma_cookie_assign(tx); if (list_empty(&mv_chan->chain)) - list_splice_init(&sw_desc->tx_list, &mv_chan->chain); + list_add_tail(&sw_desc->chain_node, &mv_chan->chain); else { new_hw_chain = 0; old_chain_tail = list_entry(mv_chan->chain.prev, struct mv_xor_desc_slot, chain_node); - list_splice_init(&grp_start->tx_list, - &old_chain_tail->chain_node); - - if (!mv_can_chain(grp_start)) - goto submit_done; + list_add_tail(&sw_desc->chain_node, &mv_chan->chain); dev_dbg(mv_chan_to_devp(mv_chan), "Append to last desc %pa\n", &old_chain_tail->async_tx.phys); /* fix up the hardware chain */ - mv_desc_set_next_desc(old_chain_tail, grp_start->async_tx.phys); + mv_desc_set_next_desc(old_chain_tail, sw_desc->async_tx.phys); /* if the channel is not busy */ if (!mv_chan_is_busy(mv_chan)) { @@ -510,9 +435,8 @@ mv_xor_tx_submit(struct dma_async_tx_descriptor *tx) } if (new_hw_chain) - mv_xor_start_new_chain(mv_chan, grp_start); + mv_xor_start_new_chain(mv_chan, sw_desc); -submit_done: spin_unlock_bh(&mv_chan->lock); return cookie; @@ -533,8 +457,9 @@ static int mv_xor_alloc_chan_resources(struct dma_chan *chan) while (idx < num_descs_in_pool) { slot = kzalloc(sizeof(*slot), GFP_KERNEL); if (!slot) { - printk(KERN_INFO "MV XOR Channel only initialized" - " %d descriptor slots", idx); + dev_info(mv_chan_to_devp(mv_chan), + "channel only initialized %d descriptor slots", + idx); break; } virt_desc = mv_chan->dma_desc_pool_virt; @@ -544,7 +469,6 @@ static int mv_xor_alloc_chan_resources(struct dma_chan *chan) slot->async_tx.tx_submit = mv_xor_tx_submit; INIT_LIST_HEAD(&slot->chain_node); INIT_LIST_HEAD(&slot->slot_node); - INIT_LIST_HEAD(&slot->tx_list); dma_desc = mv_chan->dma_desc_pool; slot->async_tx.phys = dma_desc + idx * MV_XOR_SLOT_SIZE; slot->idx = idx++; @@ -567,52 +491,12 @@ static int mv_xor_alloc_chan_resources(struct dma_chan *chan) return mv_chan->slots_allocated ? : -ENOMEM; } -static struct dma_async_tx_descriptor * -mv_xor_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, - size_t len, unsigned long flags) -{ - struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan); - struct mv_xor_desc_slot *sw_desc, *grp_start; - int slot_cnt; - - dev_dbg(mv_chan_to_devp(mv_chan), - "%s dest: %pad src %pad len: %u flags: %ld\n", - __func__, &dest, &src, len, flags); - if (unlikely(len < MV_XOR_MIN_BYTE_COUNT)) - return NULL; - - BUG_ON(len > MV_XOR_MAX_BYTE_COUNT); - - spin_lock_bh(&mv_chan->lock); - slot_cnt = mv_chan_memcpy_slot_count(len); - sw_desc = mv_xor_alloc_slots(mv_chan, slot_cnt, 1); - if (sw_desc) { - sw_desc->type = DMA_MEMCPY; - sw_desc->async_tx.flags = flags; - grp_start = sw_desc->group_head; - mv_desc_init(grp_start, flags); - mv_desc_set_byte_count(grp_start, len); - mv_desc_set_dest_addr(sw_desc->group_head, dest); - mv_desc_set_src_addr(grp_start, 0, src); - sw_desc->unmap_src_cnt = 1; - sw_desc->unmap_len = len; - } - spin_unlock_bh(&mv_chan->lock); - - dev_dbg(mv_chan_to_devp(mv_chan), - "%s sw_desc %p async_tx %p\n", - __func__, sw_desc, sw_desc ? &sw_desc->async_tx : NULL); - - return sw_desc ? &sw_desc->async_tx : NULL; -} - static struct dma_async_tx_descriptor * mv_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src, unsigned int src_cnt, size_t len, unsigned long flags) { struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan); - struct mv_xor_desc_slot *sw_desc, *grp_start; - int slot_cnt; + struct mv_xor_desc_slot *sw_desc; if (unlikely(len < MV_XOR_MIN_BYTE_COUNT)) return NULL; @@ -624,20 +508,13 @@ mv_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src, __func__, src_cnt, len, &dest, flags); spin_lock_bh(&mv_chan->lock); - slot_cnt = mv_chan_xor_slot_count(len, src_cnt); - sw_desc = mv_xor_alloc_slots(mv_chan, slot_cnt, 1); + sw_desc = mv_xor_alloc_slot(mv_chan); if (sw_desc) { sw_desc->type = DMA_XOR; sw_desc->async_tx.flags = flags; - grp_start = sw_desc->group_head; - mv_desc_init(grp_start, flags); - /* the byte count field is the same as in memcpy desc*/ - mv_desc_set_byte_count(grp_start, len); - mv_desc_set_dest_addr(sw_desc->group_head, dest); - sw_desc->unmap_src_cnt = src_cnt; - sw_desc->unmap_len = len; + mv_desc_init(sw_desc, dest, len, flags); while (src_cnt--) - mv_desc_set_src_addr(grp_start, src_cnt, src[src_cnt]); + mv_desc_set_src_addr(sw_desc, src_cnt, src[src_cnt]); } spin_unlock_bh(&mv_chan->lock); dev_dbg(mv_chan_to_devp(mv_chan), @@ -646,6 +523,35 @@ mv_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src, return sw_desc ? &sw_desc->async_tx : NULL; } +static struct dma_async_tx_descriptor * +mv_xor_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, + size_t len, unsigned long flags) +{ + /* + * A MEMCPY operation is identical to an XOR operation with only + * a single source address. + */ + return mv_xor_prep_dma_xor(chan, dest, &src, 1, len, flags); +} + +static struct dma_async_tx_descriptor * +mv_xor_prep_dma_interrupt(struct dma_chan *chan, unsigned long flags) +{ + struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan); + dma_addr_t src, dest; + size_t len; + + src = mv_chan->dummy_src_addr; + dest = mv_chan->dummy_dst_addr; + len = MV_XOR_MIN_BYTE_COUNT; + + /* + * We implement the DMA_INTERRUPT operation as a minimum sized + * XOR operation with a single dummy source address. + */ + return mv_xor_prep_dma_xor(chan, dest, &src, 1, len, flags); +} + static void mv_xor_free_chan_resources(struct dma_chan *chan) { struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan); @@ -733,18 +639,16 @@ static void mv_dump_xor_regs(struct mv_xor_chan *chan) static void mv_xor_err_interrupt_handler(struct mv_xor_chan *chan, u32 intr_cause) { - if (intr_cause & (1 << 4)) { - dev_dbg(mv_chan_to_devp(chan), - "ignore this error\n"); - return; + if (intr_cause & XOR_INT_ERR_DECODE) { + dev_dbg(mv_chan_to_devp(chan), "ignoring address decode error\n"); + return; } - dev_err(mv_chan_to_devp(chan), - "error on chan %d. intr cause 0x%08x\n", + dev_err(mv_chan_to_devp(chan), "error on chan %d. intr cause 0x%08x\n", chan->idx, intr_cause); mv_dump_xor_regs(chan); - BUG(); + WARN_ON(1); } static irqreturn_t mv_xor_interrupt_handler(int irq, void *data) @@ -754,7 +658,7 @@ static irqreturn_t mv_xor_interrupt_handler(int irq, void *data) dev_dbg(mv_chan_to_devp(chan), "intr cause %x\n", intr_cause); - if (mv_is_err_intr(intr_cause)) + if (intr_cause & XOR_INTR_ERRORS) mv_xor_err_interrupt_handler(chan, intr_cause); tasklet_schedule(&chan->irq_tasklet); @@ -1041,6 +945,10 @@ static int mv_xor_channel_remove(struct mv_xor_chan *mv_chan) dma_free_coherent(dev, MV_XOR_POOL_SIZE, mv_chan->dma_desc_pool_virt, mv_chan->dma_desc_pool); + dma_unmap_single(dev, mv_chan->dummy_src_addr, + MV_XOR_MIN_BYTE_COUNT, DMA_FROM_DEVICE); + dma_unmap_single(dev, mv_chan->dummy_dst_addr, + MV_XOR_MIN_BYTE_COUNT, DMA_TO_DEVICE); list_for_each_entry_safe(chan, _chan, &mv_chan->dmadev.channels, device_node) { @@ -1070,6 +978,16 @@ mv_xor_channel_add(struct mv_xor_device *xordev, dma_dev = &mv_chan->dmadev; + /* + * These source and destination dummy buffers are used to implement + * a DMA_INTERRUPT operation as a minimum-sized XOR operation. + * Hence, we only need to map the buffers at initialization-time. + */ + mv_chan->dummy_src_addr = dma_map_single(dma_dev->dev, + mv_chan->dummy_src, MV_XOR_MIN_BYTE_COUNT, DMA_FROM_DEVICE); + mv_chan->dummy_dst_addr = dma_map_single(dma_dev->dev, + mv_chan->dummy_dst, MV_XOR_MIN_BYTE_COUNT, DMA_TO_DEVICE); + /* allocate coherent memory for hardware descriptors * note: writecombine gives slightly better performance, but * requires that we explicitly flush the writes @@ -1094,6 +1012,8 @@ mv_xor_channel_add(struct mv_xor_device *xordev, dma_dev->dev = &pdev->dev; /* set prep routines based on capability */ + if (dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask)) + dma_dev->device_prep_dma_interrupt = mv_xor_prep_dma_interrupt; if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) dma_dev->device_prep_dma_memcpy = mv_xor_prep_dma_memcpy; if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) { @@ -1116,7 +1036,7 @@ mv_xor_channel_add(struct mv_xor_device *xordev, mv_chan_unmask_interrupts(mv_chan); - mv_set_mode(mv_chan, DMA_MEMCPY); + mv_set_mode(mv_chan, DMA_XOR); spin_lock_init(&mv_chan->lock); INIT_LIST_HEAD(&mv_chan->chain); diff --git a/drivers/dma/mv_xor.h b/drivers/dma/mv_xor.h index d0749229c875187a454c498964155c37e765d314..78edc7e44569f468da8a5fcb8e84abcc9dc5faf0 100644 --- a/drivers/dma/mv_xor.h +++ b/drivers/dma/mv_xor.h @@ -23,17 +23,22 @@ #include #include -#define USE_TIMER #define MV_XOR_POOL_SIZE PAGE_SIZE #define MV_XOR_SLOT_SIZE 64 #define MV_XOR_THRESHOLD 1 #define MV_XOR_MAX_CHANNELS 2 +#define MV_XOR_MIN_BYTE_COUNT SZ_128 +#define MV_XOR_MAX_BYTE_COUNT (SZ_16M - 1) + /* Values for the XOR_CONFIG register */ #define XOR_OPERATION_MODE_XOR 0 #define XOR_OPERATION_MODE_MEMCPY 2 #define XOR_DESCRIPTOR_SWAP BIT(14) +#define XOR_DESC_DMA_OWNED BIT(31) +#define XOR_DESC_EOD_INT_EN BIT(31) + #define XOR_CURR_DESC(chan) (chan->mmr_high_base + 0x10 + (chan->idx * 4)) #define XOR_NEXT_DESC(chan) (chan->mmr_high_base + 0x00 + (chan->idx * 4)) #define XOR_BYTE_COUNT(chan) (chan->mmr_high_base + 0x20 + (chan->idx * 4)) @@ -48,7 +53,24 @@ #define XOR_INTR_MASK(chan) (chan->mmr_base + 0x40) #define XOR_ERROR_CAUSE(chan) (chan->mmr_base + 0x50) #define XOR_ERROR_ADDR(chan) (chan->mmr_base + 0x60) -#define XOR_INTR_MASK_VALUE 0x3F5 + +#define XOR_INT_END_OF_DESC BIT(0) +#define XOR_INT_END_OF_CHAIN BIT(1) +#define XOR_INT_STOPPED BIT(2) +#define XOR_INT_PAUSED BIT(3) +#define XOR_INT_ERR_DECODE BIT(4) +#define XOR_INT_ERR_RDPROT BIT(5) +#define XOR_INT_ERR_WRPROT BIT(6) +#define XOR_INT_ERR_OWN BIT(7) +#define XOR_INT_ERR_PAR BIT(8) +#define XOR_INT_ERR_MBUS BIT(9) + +#define XOR_INTR_ERRORS (XOR_INT_ERR_DECODE | XOR_INT_ERR_RDPROT | \ + XOR_INT_ERR_WRPROT | XOR_INT_ERR_OWN | \ + XOR_INT_ERR_PAR | XOR_INT_ERR_MBUS) + +#define XOR_INTR_MASK_VALUE (XOR_INT_END_OF_DESC | XOR_INT_END_OF_CHAIN | \ + XOR_INT_STOPPED | XOR_INTR_ERRORS) #define WINDOW_BASE(w) (0x50 + ((w) << 2)) #define WINDOW_SIZE(w) (0x70 + ((w) << 2)) @@ -97,10 +119,9 @@ struct mv_xor_chan { struct list_head all_slots; int slots_allocated; struct tasklet_struct irq_tasklet; -#ifdef USE_TIMER - unsigned long cleanup_time; - u32 current_on_last_cleanup; -#endif + char dummy_src[MV_XOR_MIN_BYTE_COUNT]; + char dummy_dst[MV_XOR_MIN_BYTE_COUNT]; + dma_addr_t dummy_src_addr, dummy_dst_addr; }; /** @@ -110,16 +131,10 @@ struct mv_xor_chan { * @completed_node: node on the mv_xor_chan.completed_slots list * @hw_desc: virtual address of the hardware descriptor chain * @phys: hardware address of the hardware descriptor chain - * @group_head: first operation in a transaction - * @slot_cnt: total slots used in an transaction (group of operations) - * @slots_per_op: number of slots per operation + * @slot_used: slot in use or not * @idx: pool index - * @unmap_src_cnt: number of xor sources - * @unmap_len: transaction bytecount * @tx_list: list of slots that make up a multi-descriptor transaction * @async_tx: support for the async_tx api - * @xor_check_result: result of zero sum - * @crc32_result: result crc calculation */ struct mv_xor_desc_slot { struct list_head slot_node; @@ -127,23 +142,9 @@ struct mv_xor_desc_slot { struct list_head completed_node; enum dma_transaction_type type; void *hw_desc; - struct mv_xor_desc_slot *group_head; - u16 slot_cnt; - u16 slots_per_op; + u16 slot_used; u16 idx; - u16 unmap_src_cnt; - u32 value; - size_t unmap_len; - struct list_head tx_list; struct dma_async_tx_descriptor async_tx; - union { - u32 *xor_check_result; - u32 *crc32_result; - }; -#ifdef USE_TIMER - unsigned long arrival_time; - struct timer_list timeout; -#endif }; /* @@ -189,9 +190,4 @@ struct mv_xor_desc { #define mv_hw_desc_slot_idx(hw_desc, idx) \ ((void *)(((unsigned long)hw_desc) + ((idx) << 5))) -#define MV_XOR_MIN_BYTE_COUNT (128) -#define XOR_MAX_BYTE_COUNT ((16 * 1024 * 1024) - 1) -#define MV_XOR_MAX_BYTE_COUNT XOR_MAX_BYTE_COUNT - - #endif diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index d5149aacd2feefdb9a3d516187e6f5b03f084ada..4839bfa74a107a1ad4cbdfb09017d86da1fd1a41 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -1367,17 +1367,10 @@ static int pl330_submit_req(struct pl330_thread *thrd, struct pl330_dmac *pl330 = thrd->dmac; struct _xfer_spec xs; unsigned long flags; - void __iomem *regs; unsigned idx; u32 ccr; int ret = 0; - /* No Req or Unacquired Channel or DMAC */ - if (!desc || !thrd || thrd->free) - return -EINVAL; - - regs = thrd->dmac->base; - if (pl330->state == DYING || pl330->dmac_tbd.reset_chan & (1 << thrd->id)) { dev_info(thrd->dmac->ddma.dev, "%s:%d\n", @@ -2755,8 +2748,10 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) list_del(&pch->chan.device_node); /* Flush the channel */ - pl330_control(&pch->chan, DMA_TERMINATE_ALL, 0); - pl330_free_chan_resources(&pch->chan); + if (pch->thread) { + pl330_control(&pch->chan, DMA_TERMINATE_ALL, 0); + pl330_free_chan_resources(&pch->chan); + } } probe_err2: pl330_del(pl330); @@ -2782,8 +2777,10 @@ static int pl330_remove(struct amba_device *adev) list_del(&pch->chan.device_node); /* Flush the channel */ - pl330_control(&pch->chan, DMA_TERMINATE_ALL, 0); - pl330_free_chan_resources(&pch->chan); + if (pch->thread) { + pl330_control(&pch->chan, DMA_TERMINATE_ALL, 0); + pl330_free_chan_resources(&pch->chan); + } } pl330_del(pl330); diff --git a/drivers/dma/sh/rcar-audmapp.c b/drivers/dma/sh/rcar-audmapp.c index dabbf0aba2e9b5195625ffee4e8508bef3e087b4..80fd2aeb4870f07f78214e2c554181d7bb6bc16d 100644 --- a/drivers/dma/sh/rcar-audmapp.c +++ b/drivers/dma/sh/rcar-audmapp.c @@ -117,7 +117,7 @@ static void audmapp_start_xfer(struct shdma_chan *schan, audmapp_write(auchan, chcr, PDMACHCR); } -static void audmapp_get_config(struct audmapp_chan *auchan, int slave_id, +static int audmapp_get_config(struct audmapp_chan *auchan, int slave_id, u32 *chcr, dma_addr_t *dst) { struct audmapp_device *audev = to_dev(auchan); @@ -131,20 +131,22 @@ static void audmapp_get_config(struct audmapp_chan *auchan, int slave_id, if (!pdata) { /* DT */ *chcr = ((u32)slave_id) << 16; auchan->shdma_chan.slave_id = (slave_id) >> 8; - return; + return 0; } /* non-DT */ if (slave_id >= AUDMAPP_SLAVE_NUMBER) - return; + return -ENXIO; for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++) if (cfg->slave_id == slave_id) { *chcr = cfg->chcr; *dst = cfg->dst; - break; + return 0; } + + return -ENXIO; } static int audmapp_set_slave(struct shdma_chan *schan, int slave_id, @@ -153,8 +155,11 @@ static int audmapp_set_slave(struct shdma_chan *schan, int slave_id, struct audmapp_chan *auchan = to_chan(schan); u32 chcr; dma_addr_t dst; + int ret; - audmapp_get_config(auchan, slave_id, &chcr, &dst); + ret = audmapp_get_config(auchan, slave_id, &chcr, &dst); + if (ret < 0) + return ret; if (try) return 0; diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 1f92a56fd2b6c38279872a2257d997b9ec2ff9ca..3aa10b32825491dce9d5c0ad244ddc53f78df12e 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -862,7 +862,6 @@ static int sun6i_dma_probe(struct platform_device *pdev) { struct sun6i_dma_dev *sdc; struct resource *res; - struct clk *mux, *pll6; int ret, i; sdc = devm_kzalloc(&pdev->dev, sizeof(*sdc), GFP_KERNEL); @@ -886,28 +885,6 @@ static int sun6i_dma_probe(struct platform_device *pdev) return PTR_ERR(sdc->clk); } - mux = clk_get(NULL, "ahb1_mux"); - if (IS_ERR(mux)) { - dev_err(&pdev->dev, "Couldn't get AHB1 Mux\n"); - return PTR_ERR(mux); - } - - pll6 = clk_get(NULL, "pll6"); - if (IS_ERR(pll6)) { - dev_err(&pdev->dev, "Couldn't get PLL6\n"); - clk_put(mux); - return PTR_ERR(pll6); - } - - ret = clk_set_parent(mux, pll6); - clk_put(pll6); - clk_put(mux); - - if (ret) { - dev_err(&pdev->dev, "Couldn't reparent AHB1 on PLL6\n"); - return ret; - } - sdc->rstc = devm_reset_control_get(&pdev->dev, NULL); if (IS_ERR(sdc->rstc)) { dev_err(&pdev->dev, "No reset controller specified\n"); diff --git a/drivers/dma/xilinx/xilinx_vdma.c b/drivers/dma/xilinx/xilinx_vdma.c index 42a13e8d460781492486d94c5708fafb2f659e3d..a6e64767186e7d24e0b241dc97f3225196c85c39 100644 --- a/drivers/dma/xilinx/xilinx_vdma.c +++ b/drivers/dma/xilinx/xilinx_vdma.c @@ -1365,7 +1365,6 @@ static const struct of_device_id xilinx_vdma_of_ids[] = { static struct platform_driver xilinx_vdma_driver = { .driver = { .name = "xilinx-vdma", - .owner = THIS_MODULE, .of_match_table = xilinx_vdma_of_ids, }, .probe = xilinx_vdma_probe, diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index b066bb3ca01ace734e16abd61fccfb88cdd46c0c..e3b4b0f02b3d1de3e07a9d40cb11b22d1822701f 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -8,6 +8,7 @@ menuconfig DRM tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)" depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && MMU && HAS_DMA select HDMI + select FB_CMDLINE select I2C select I2C_ALGOBIT select DMA_SHARED_BUFFER @@ -24,12 +25,6 @@ config DRM_MIPI_DSI bool depends on DRM -config DRM_USB - tristate - depends on DRM - depends on USB_SUPPORT && USB_ARCH_HAS_HCD - select USB - config DRM_KMS_HELPER tristate depends on DRM @@ -115,6 +110,7 @@ config DRM_RADEON select HWMON select BACKLIGHT_CLASS_DEVICE select INTERVAL_TREE + select MMU_NOTIFIER help Choose this option if you have an ATI Radeon graphics card. There are both PCI and AGP versions. You don't need to choose this to diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 4a55d59ccd22f21ed3c11f13bc92ab9761f22eae..9292a761ea6db9fa2844f762726b717e1bbce4b2 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -4,7 +4,7 @@ ccflags-y := -Iinclude/drm -drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ +drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm_context.o drm_dma.o \ drm_fops.o drm_gem.o drm_ioctl.o drm_irq.o \ drm_lock.o drm_memory.o drm_drv.o drm_vm.o \ @@ -22,8 +22,6 @@ drm-$(CONFIG_PCI) += ati_pcigart.o drm-$(CONFIG_DRM_PANEL) += drm_panel.o drm-$(CONFIG_OF) += drm_of.o -drm-usb-y := drm_usb.o - drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ drm_plane_helper.o drm_dp_mst_topology.o drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o @@ -36,7 +34,6 @@ CFLAGS_drm_trace_points.o := -I$(src) obj-$(CONFIG_DRM) += drm.o obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o -obj-$(CONFIG_DRM_USB) += drm_usb.o obj-$(CONFIG_DRM_TTM) += ttm/ obj-$(CONFIG_DRM_TDFX) += tdfx/ obj-$(CONFIG_DRM_R128) += r128/ diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index e2d5792b140fb41069713661612e0853da0e5854..f672e6ad8afae05d42ebeb31b31bc9c3eb60c117 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -308,6 +308,7 @@ static struct drm_driver armada_drm_driver = { .postclose = NULL, .lastclose = armada_drm_lastclose, .unload = armada_drm_unload, + .set_busid = drm_platform_set_busid, .get_vblank_counter = drm_vblank_count, .enable_vblank = armada_drm_enable_vblank, .disable_vblank = armada_drm_disable_vblank, diff --git a/drivers/gpu/drm/armada/armada_gem.h b/drivers/gpu/drm/armada/armada_gem.h index 00b6cd461a03c9c1caf91b7af5264adf46c8a91e..b000ea3a829af66ca88cbaf1d314267d95623e33 100644 --- a/drivers/gpu/drm/armada/armada_gem.h +++ b/drivers/gpu/drm/armada/armada_gem.h @@ -8,6 +8,8 @@ #ifndef ARMADA_GEM_H #define ARMADA_GEM_H +#include + /* GEM */ struct armada_gem_object { struct drm_gem_object obj; diff --git a/drivers/gpu/drm/ast/ast_dp501.c b/drivers/gpu/drm/ast/ast_dp501.c index 5da4b62285fa2c690b5876e4265971b5dfd1640d..76f07f38b941f5e8ea3ad9255e8870b293bfab84 100644 --- a/drivers/gpu/drm/ast/ast_dp501.c +++ b/drivers/gpu/drm/ast/ast_dp501.c @@ -379,11 +379,39 @@ static bool ast_init_dvo(struct drm_device *dev) return true; } + +static void ast_init_analog(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + u32 data; + + /* + * Set DAC source to VGA mode in SCU2C via the P2A + * bridge. First configure the P2U to target the SCU + * in case it isn't at this stage. + */ + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + + /* Then unlock the SCU with the magic password */ + ast_write32(ast, 0x12000, 0x1688a8a8); + ast_write32(ast, 0x12000, 0x1688a8a8); + ast_write32(ast, 0x12000, 0x1688a8a8); + + /* Finally, clear bits [17:16] of SCU2c */ + data = ast_read32(ast, 0x1202c); + data &= 0xfffcffff; + ast_write32(ast, 0, data); + + /* Disable DVO */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x00); +} + void ast_init_3rdtx(struct drm_device *dev) { struct ast_private *ast = dev->dev_private; u8 jreg; - u32 data; + if (ast->chip == AST2300 || ast->chip == AST2400) { jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff); switch (jreg & 0x0e) { @@ -399,12 +427,8 @@ void ast_init_3rdtx(struct drm_device *dev) default: if (ast->tx_chip_type == AST_TX_SIL164) ast_init_dvo(dev); - else { - ast_write32(ast, 0x12000, 0x1688a8a8); - data = ast_read32(ast, 0x1202c); - data &= 0xfffcffff; - ast_write32(ast, 0, data); - } + else + ast_init_analog(dev); } } } diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index f19682a93c243e3775fa96b6b0750334b97dd95f..9a32d9dfdd269aaa1918e4340b660b5b9567ef4d 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -199,6 +199,7 @@ static struct drm_driver driver = { .load = ast_driver_load, .unload = ast_driver_unload, + .set_busid = drm_pci_set_busid, .fops = &ast_fops, .name = DRIVER_NAME, diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 957d4fabf1e1c2c8e58348a9af7f6ba6fcdd63c6..86205a28e56b79ceeb247949343f7117a08c393c 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -36,6 +36,8 @@ #include #include +#include + #include #include @@ -125,8 +127,9 @@ struct ast_gem_object; #define AST_IO_AR_PORT_WRITE (0x40) #define AST_IO_MISC_PORT_WRITE (0x42) +#define AST_IO_VGA_ENABLE_PORT (0x43) #define AST_IO_SEQ_PORT (0x44) -#define AST_DAC_INDEX_READ (0x3c7) +#define AST_IO_DAC_INDEX_READ (0x47) #define AST_IO_DAC_INDEX_WRITE (0x48) #define AST_IO_DAC_DATA (0x49) #define AST_IO_GR_PORT (0x4E) @@ -134,6 +137,8 @@ struct ast_gem_object; #define AST_IO_INPUT_STATUS1_READ (0x5A) #define AST_IO_MISC_PORT_READ (0x4C) +#define AST_IO_MM_OFFSET (0x380) + #define __ast_read(x) \ static inline u##x ast_read##x(struct ast_private *ast, u32 reg) { \ u##x val = 0;\ @@ -316,7 +321,7 @@ struct ast_bo { struct ttm_placement placement; struct ttm_bo_kmap_obj kmap; struct drm_gem_object gem; - u32 placements[3]; + struct ttm_place placements[3]; int pin_count; }; #define gem_to_ast_bo(gobj) container_of((gobj), struct ast_bo, gem) @@ -381,6 +386,9 @@ int ast_bo_push_sysram(struct ast_bo *bo); int ast_mmap(struct file *filp, struct vm_area_struct *vma); /* ast post */ +void ast_enable_vga(struct drm_device *dev); +void ast_enable_mmio(struct drm_device *dev); +bool ast_is_vga_enabled(struct drm_device *dev); void ast_post_gpu(struct drm_device *dev); u32 ast_mindwm(struct ast_private *ast, u32 r); void ast_moutdwm(struct ast_private *ast, u32 r, u32 v); diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index cba45c77455279ff0aaaad8974e815b06e9b9f5c..5c60ae524c454952316f9d8f8864cf8e52ce8cc4 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -186,7 +186,8 @@ static int astfb_create_object(struct ast_fbdev *afbdev, static int astfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { - struct ast_fbdev *afbdev = (struct ast_fbdev *)helper; + struct ast_fbdev *afbdev = + container_of(helper, struct ast_fbdev, helper); struct drm_device *dev = afbdev->helper.dev; struct drm_mode_fb_cmd2 mode_cmd; struct drm_framebuffer *fb; diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index b792194e0d9ceb6320457c99f270a67680a2d6d8..035dacc93382f0923a9d52c645a70cc192a6756e 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -63,7 +63,7 @@ uint8_t ast_get_index_reg_mask(struct ast_private *ast, } -static int ast_detect_chip(struct drm_device *dev) +static int ast_detect_chip(struct drm_device *dev, bool *need_post) { struct ast_private *ast = dev->dev_private; uint32_t data, jreg; @@ -110,6 +110,21 @@ static int ast_detect_chip(struct drm_device *dev) } } + /* + * If VGA isn't enabled, we need to enable now or subsequent + * access to the scratch registers will fail. We also inform + * our caller that it needs to POST the chip + * (Assumption: VGA not enabled -> need to POST) + */ + if (!ast_is_vga_enabled(dev)) { + ast_enable_vga(dev); + ast_enable_mmio(dev); + DRM_INFO("VGA not enabled on entry, requesting chip POST\n"); + *need_post = true; + } else + *need_post = false; + + /* Check if we support wide screen */ switch (ast->chip) { case AST1180: ast->support_wide_screen = true; @@ -125,6 +140,7 @@ static int ast_detect_chip(struct drm_device *dev) ast->support_wide_screen = true; else { ast->support_wide_screen = false; + /* Read SCU7c (silicon revision register) */ ast_write32(ast, 0xf004, 0x1e6e0000); ast_write32(ast, 0xf000, 0x1); data = ast_read32(ast, 0x1207c); @@ -137,11 +153,29 @@ static int ast_detect_chip(struct drm_device *dev) break; } + /* Check 3rd Tx option (digital output afaik) */ ast->tx_chip_type = AST_TX_NONE; - jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xff); - if (jreg & 0x80) - ast->tx_chip_type = AST_TX_SIL164; + + /* + * VGACRA3 Enhanced Color Mode Register, check if DVO is already + * enabled, in that case, assume we have a SIL164 TMDS transmitter + * + * Don't make that assumption if we the chip wasn't enabled and + * is at power-on reset, otherwise we'll incorrectly "detect" a + * SIL164 when there is none. + */ + if (!*need_post) { + jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xff); + if (jreg & 0x80) + ast->tx_chip_type = AST_TX_SIL164; + } + if ((ast->chip == AST2300) || (ast->chip == AST2400)) { + /* + * On AST2300 and 2400, look the configuration set by the SoC in + * the SOC scratch register #1 bits 11:8 (interestingly marked + * as "reserved" in the spec) + */ jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff); switch (jreg) { case 0x04: @@ -162,6 +196,17 @@ static int ast_detect_chip(struct drm_device *dev) } } + /* Print stuff for diagnostic purposes */ + switch(ast->tx_chip_type) { + case AST_TX_SIL164: + DRM_INFO("Using Sil164 TMDS transmitter\n"); + break; + case AST_TX_DP501: + DRM_INFO("Using DP501 DisplayPort transmitter\n"); + break; + default: + DRM_INFO("Analog VGA only\n"); + } return 0; } @@ -346,6 +391,7 @@ static u32 ast_get_vram_info(struct drm_device *dev) int ast_driver_load(struct drm_device *dev, unsigned long flags) { struct ast_private *ast; + bool need_post; int ret = 0; ast = kzalloc(sizeof(struct ast_private), GFP_KERNEL); @@ -360,13 +406,27 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags) ret = -EIO; goto out_free; } - ast->ioregs = pci_iomap(dev->pdev, 2, 0); + + /* + * If we don't have IO space at all, use MMIO now and + * assume the chip has MMIO enabled by default (rev 0x20 + * and higher). + */ + if (!(pci_resource_flags(dev->pdev, 2) & IORESOURCE_IO)) { + DRM_INFO("platform has no IO space, trying MMIO\n"); + ast->ioregs = ast->regs + AST_IO_MM_OFFSET; + } + + /* "map" IO regs if the above hasn't done so already */ if (!ast->ioregs) { - ret = -EIO; - goto out_free; + ast->ioregs = pci_iomap(dev->pdev, 2, 0); + if (!ast->ioregs) { + ret = -EIO; + goto out_free; + } } - ast_detect_chip(dev); + ast_detect_chip(dev, &need_post); if (ast->chip != AST1180) { ast_get_dram_info(dev); @@ -374,6 +434,9 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags) DRM_INFO("dram %d %d %d %08x\n", ast->mclk, ast->dram_type, ast->dram_bus_width, ast->vram_size); } + if (need_post) + ast_post_gpu(dev); + ret = ast_mm_init(ast); if (ret) goto out_free; diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 5389350244f216fac07c5d98723ee2632c627e89..9dc0fd5c1ea4ef7741eaad8e85a65ea280096caf 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -80,6 +80,8 @@ static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mo struct ast_private *ast = crtc->dev->dev_private; u32 refresh_rate_index = 0, mode_id, color_index, refresh_rate; u32 hborder, vborder; + bool check_sync; + struct ast_vbios_enhtable *best = NULL; switch (crtc->primary->fb->bits_per_pixel) { case 8: @@ -141,14 +143,34 @@ static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mo } refresh_rate = drm_mode_vrefresh(mode); - while (vbios_mode->enh_table->refresh_rate < refresh_rate) { - vbios_mode->enh_table++; - if ((vbios_mode->enh_table->refresh_rate > refresh_rate) || - (vbios_mode->enh_table->refresh_rate == 0xff)) { - vbios_mode->enh_table--; - break; + check_sync = vbios_mode->enh_table->flags & WideScreenMode; + do { + struct ast_vbios_enhtable *loop = vbios_mode->enh_table; + + while (loop->refresh_rate != 0xff) { + if ((check_sync) && + (((mode->flags & DRM_MODE_FLAG_NVSYNC) && + (loop->flags & PVSync)) || + ((mode->flags & DRM_MODE_FLAG_PVSYNC) && + (loop->flags & NVSync)) || + ((mode->flags & DRM_MODE_FLAG_NHSYNC) && + (loop->flags & PHSync)) || + ((mode->flags & DRM_MODE_FLAG_PHSYNC) && + (loop->flags & NHSync)))) { + loop++; + continue; + } + if (loop->refresh_rate <= refresh_rate + && (!best || loop->refresh_rate > best->refresh_rate)) + best = loop; + loop++; } - } + if (best || !check_sync) + break; + check_sync = 0; + } while (1); + if (best) + vbios_mode->enh_table = best; hborder = (vbios_mode->enh_table->flags & HBorder) ? 8 : 0; vborder = (vbios_mode->enh_table->flags & VBorder) ? 8 : 0; @@ -419,8 +441,10 @@ static void ast_set_sync_reg(struct drm_device *dev, struct drm_display_mode *mo struct ast_private *ast = dev->dev_private; u8 jreg; - jreg = ast_io_read8(ast, AST_IO_MISC_PORT_READ); - jreg |= (vbios_mode->enh_table->flags & SyncNN); + jreg = ast_io_read8(ast, AST_IO_MISC_PORT_READ); + jreg &= ~0xC0; + if (vbios_mode->enh_table->flags & NVSync) jreg |= 0x80; + if (vbios_mode->enh_table->flags & NHSync) jreg |= 0x40; ast_io_write8(ast, AST_IO_MISC_PORT_WRITE, jreg); } @@ -1080,8 +1104,8 @@ static u32 copy_cursor_image(u8 *src, u8 *dst, int width, int height) srcdata32[1].ul = *((u32 *)(srcxor + 4)) & 0xf0f0f0f0; data32.b[0] = srcdata32[0].b[1] | (srcdata32[0].b[0] >> 4); data32.b[1] = srcdata32[0].b[3] | (srcdata32[0].b[2] >> 4); - data32.b[2] = srcdata32[0].b[1] | (srcdata32[1].b[0] >> 4); - data32.b[3] = srcdata32[0].b[3] | (srcdata32[1].b[2] >> 4); + data32.b[2] = srcdata32[1].b[1] | (srcdata32[1].b[0] >> 4); + data32.b[3] = srcdata32[1].b[3] | (srcdata32[1].b[2] >> 4); writel(data32.ul, dstxor); csum += data32.ul; diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c index 38d437f3a267493fd097da14aa0a05d6cda68a99..810c51d92b99f81a441f37d02e319ed25b7768e5 100644 --- a/drivers/gpu/drm/ast/ast_post.c +++ b/drivers/gpu/drm/ast/ast_post.c @@ -33,18 +33,23 @@ static void ast_init_dram_2300(struct drm_device *dev); -static void -ast_enable_vga(struct drm_device *dev) +void ast_enable_vga(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + + ast_io_write8(ast, AST_IO_VGA_ENABLE_PORT, 0x01); + ast_io_write8(ast, AST_IO_MISC_PORT_WRITE, 0x01); +} + +void ast_enable_mmio(struct drm_device *dev) { struct ast_private *ast = dev->dev_private; - ast_io_write8(ast, 0x43, 0x01); - ast_io_write8(ast, 0x42, 0x01); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa1, 0xff, 0x04); } -#if 0 /* will use later */ -static bool -ast_is_vga_enabled(struct drm_device *dev) + +bool ast_is_vga_enabled(struct drm_device *dev) { struct ast_private *ast = dev->dev_private; u8 ch; @@ -52,7 +57,7 @@ ast_is_vga_enabled(struct drm_device *dev) if (ast->chip == AST1180) { /* TODO 1180 */ } else { - ch = ast_io_read8(ast, 0x43); + ch = ast_io_read8(ast, AST_IO_VGA_ENABLE_PORT); if (ch) { ast_open_key(ast); ch = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff); @@ -61,7 +66,6 @@ ast_is_vga_enabled(struct drm_device *dev) } return 0; } -#endif static const u8 extreginfo[] = { 0x0f, 0x04, 0x1c, 0xff }; static const u8 extreginfo_ast2300a0[] = { 0x0f, 0x04, 0x1c, 0xff }; @@ -371,6 +375,7 @@ void ast_post_gpu(struct drm_device *dev) pci_write_config_dword(ast->dev->pdev, 0x04, reg); ast_enable_vga(dev); + ast_enable_mmio(dev); ast_open_key(ast); ast_set_def_ext_reg(dev); diff --git a/drivers/gpu/drm/ast/ast_tables.h b/drivers/gpu/drm/ast/ast_tables.h index 05c01ea8529487c9d37f32cd31237291728c82c9..3608d5aa7451a458a07fcfe3199ce3984108992b 100644 --- a/drivers/gpu/drm/ast/ast_tables.h +++ b/drivers/gpu/drm/ast/ast_tables.h @@ -35,14 +35,18 @@ #define HalfDCLK 0x00000002 #define DoubleScanMode 0x00000004 #define LineCompareOff 0x00000008 -#define SyncPP 0x00000000 -#define SyncPN 0x00000040 -#define SyncNP 0x00000080 -#define SyncNN 0x000000C0 #define HBorder 0x00000020 #define VBorder 0x00000010 #define WideScreenMode 0x00000100 #define NewModeInfo 0x00000200 +#define NHSync 0x00000400 +#define PHSync 0x00000800 +#define NVSync 0x00001000 +#define PVSync 0x00002000 +#define SyncPP (PVSync | PHSync) +#define SyncPN (PVSync | NHSync) +#define SyncNP (NVSync | PHSync) +#define SyncNN (NVSync | NHSync) /* DCLK Index */ #define VCLK25_175 0x00 @@ -72,6 +76,7 @@ #define VCLK119 0x17 #define VCLK85_5 0x18 #define VCLK97_75 0x19 +#define VCLK118_25 0x1A static struct ast_vbios_dclk_info dclk_table[] = { {0x2C, 0xE7, 0x03}, /* 00: VCLK25_175 */ @@ -100,6 +105,7 @@ static struct ast_vbios_dclk_info dclk_table[] = { {0x77, 0x58, 0x80}, /* 17: VCLK119 */ {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */ {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */ + {0x3b, 0x2c, 0x81}, /* 1A: VCLK118_25 */ }; static struct ast_vbios_stdtable vbios_stdtable[] = { @@ -246,8 +252,10 @@ static struct ast_vbios_enhtable res_1360x768[] = { static struct ast_vbios_enhtable res_1600x900[] = { {1760, 1600, 48, 32, 926, 900, 3, 5, VCLK97_75, /* 60Hz CVT RB */ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x3A }, - {1760, 1600, 48, 32, 926, 900, 3, 5, VCLK97_75, /* end */ - (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x3A } + {2112, 1600, 88,168, 934, 900, 3, 5, VCLK118_25, /* 60Hz CVT */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x3A }, + {2112, 1600, 88,168, 934, 900, 3, 5, VCLK118_25, /* 60Hz CVT */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x3A }, }; static struct ast_vbios_enhtable res_1920x1080[] = { @@ -261,11 +269,11 @@ static struct ast_vbios_enhtable res_1920x1080[] = { /* 16:10 */ static struct ast_vbios_enhtable res_1280x800[] = { {1440, 1280, 48, 32, 823, 800, 3, 6, VCLK71, /* 60Hz RB */ - (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 35 }, + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x35 }, {1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */ - (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x35 }, + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x35 }, {1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */ - (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x35 }, + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x35 }, }; @@ -273,24 +281,24 @@ static struct ast_vbios_enhtable res_1440x900[] = { {1600, 1440, 48, 32, 926, 900, 3, 6, VCLK88_75, /* 60Hz RB */ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x36 }, {1904, 1440, 80,152, 934, 900, 3, 6, VCLK106_5, /* 60Hz */ - (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x36 }, + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x36 }, {1904, 1440, 80,152, 934, 900, 3, 6, VCLK106_5, /* 60Hz */ - (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x36 }, + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x36 }, }; static struct ast_vbios_enhtable res_1680x1050[] = { {1840, 1680, 48, 32, 1080, 1050, 3, 6, VCLK119, /* 60Hz RB */ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x37 }, {2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25, /* 60Hz */ - (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x37 }, + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x37 }, {2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25, /* 60Hz */ - (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x37 }, + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x37 }, }; static struct ast_vbios_enhtable res_1920x1200[] = { - {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */ + {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz RB*/ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x34 }, - {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */ + {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz RB */ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x34 }, }; diff --git a/drivers/gpu/drm/ast/ast_ttm.c b/drivers/gpu/drm/ast/ast_ttm.c index b8246227bab009e042ebb46e3a4bb093301ffcec..08f82eae69398509a7daf55d55a4a94a4e57b6f4 100644 --- a/drivers/gpu/drm/ast/ast_ttm.c +++ b/drivers/gpu/drm/ast/ast_ttm.c @@ -293,18 +293,22 @@ void ast_mm_fini(struct ast_private *ast) void ast_ttm_placement(struct ast_bo *bo, int domain) { u32 c = 0; - bo->placement.fpfn = 0; - bo->placement.lpfn = 0; + unsigned i; + bo->placement.placement = bo->placements; bo->placement.busy_placement = bo->placements; if (domain & TTM_PL_FLAG_VRAM) - bo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM; + bo->placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM; if (domain & TTM_PL_FLAG_SYSTEM) - bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + bo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM; if (!c) - bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + bo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM; bo->placement.num_placement = c; bo->placement.num_busy_placement = c; + for (i = 0; i < c; ++i) { + bo->placements[i].fpfn = 0; + bo->placements[i].lpfn = 0; + } } int ast_bo_create(struct drm_device *dev, int size, int align, @@ -335,7 +339,7 @@ int ast_bo_create(struct drm_device *dev, int size, int align, ret = ttm_bo_init(&ast->ttm.bdev, &astbo->bo, size, ttm_bo_type_device, &astbo->placement, align >> PAGE_SHIFT, false, NULL, acc_size, - NULL, ast_bo_ttm_destroy); + NULL, NULL, ast_bo_ttm_destroy); if (ret) return ret; @@ -360,7 +364,7 @@ int ast_bo_pin(struct ast_bo *bo, u32 pl_flag, u64 *gpu_addr) ast_ttm_placement(bo, pl_flag); for (i = 0; i < bo->placement.num_placement; i++) - bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) return ret; @@ -383,7 +387,7 @@ int ast_bo_unpin(struct ast_bo *bo) return 0; for (i = 0; i < bo->placement.num_placement ; i++) - bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT; + bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) return ret; @@ -407,7 +411,7 @@ int ast_bo_push_sysram(struct ast_bo *bo) ast_ttm_placement(bo, TTM_PL_FLAG_SYSTEM); for (i = 0; i < bo->placement.num_placement ; i++) - bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) { @@ -423,7 +427,7 @@ int ast_mmap(struct file *filp, struct vm_area_struct *vma) struct ast_private *ast; if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) - return drm_mmap(filp, vma); + return -EINVAL; file_priv = filp->private_data; ast = file_priv->minor->dev->dev_private; diff --git a/drivers/gpu/drm/ati_pcigart.c b/drivers/gpu/drm/ati_pcigart.c index c399dea27a3b0e2b74913a7369075070cf4d720e..6c4d4b6eba80a94e1cd4d42f746ae22c63aae80c 100644 --- a/drivers/gpu/drm/ati_pcigart.c +++ b/drivers/gpu/drm/ati_pcigart.c @@ -34,6 +34,8 @@ #include #include +#include + # define ATI_PCIGART_PAGE_SIZE 4096 /**< PCI GART page size */ static int drm_ati_alloc_pcigart_table(struct drm_device *dev, diff --git a/drivers/gpu/drm/bochs/bochs.h b/drivers/gpu/drm/bochs/bochs.h index 7eb52dd44b0109340c7add338c5e0babb175dd79..71f2687fc3ccc44fff4dd555cdfac7dd9f63d2b9 100644 --- a/drivers/gpu/drm/bochs/bochs.h +++ b/drivers/gpu/drm/bochs/bochs.h @@ -7,6 +7,8 @@ #include #include +#include + #include #include @@ -99,7 +101,7 @@ struct bochs_bo { struct ttm_placement placement; struct ttm_bo_kmap_obj kmap; struct drm_gem_object gem; - u32 placements[3]; + struct ttm_place placements[3]; int pin_count; }; diff --git a/drivers/gpu/drm/bochs/bochs_drv.c b/drivers/gpu/drm/bochs/bochs_drv.c index 9738e9b14708d7a59b549dc1717c6f8716fefeff..98837bde2d2528356c2764b27fe5ac3721c51817 100644 --- a/drivers/gpu/drm/bochs/bochs_drv.c +++ b/drivers/gpu/drm/bochs/bochs_drv.c @@ -82,6 +82,7 @@ static struct drm_driver bochs_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET, .load = bochs_load, .unload = bochs_unload, + .set_busid = drm_pci_set_busid, .fops = &bochs_fops, .name = "bochs-drm", .desc = "bochs dispi vga interface (qemu stdvga)", diff --git a/drivers/gpu/drm/bochs/bochs_mm.c b/drivers/gpu/drm/bochs/bochs_mm.c index 1728a1b0b81376ed43e7f39fb5f863cba7563166..66286ff518d4be28e8b71b2b8ebf0d0b2ada6496 100644 --- a/drivers/gpu/drm/bochs/bochs_mm.c +++ b/drivers/gpu/drm/bochs/bochs_mm.c @@ -257,20 +257,26 @@ void bochs_mm_fini(struct bochs_device *bochs) static void bochs_ttm_placement(struct bochs_bo *bo, int domain) { + unsigned i; u32 c = 0; - bo->placement.fpfn = 0; - bo->placement.lpfn = 0; bo->placement.placement = bo->placements; bo->placement.busy_placement = bo->placements; if (domain & TTM_PL_FLAG_VRAM) { - bo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED + bo->placements[c++].flags = TTM_PL_FLAG_WC + | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM; } if (domain & TTM_PL_FLAG_SYSTEM) { - bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + bo->placements[c++].flags = TTM_PL_MASK_CACHING + | TTM_PL_FLAG_SYSTEM; } if (!c) { - bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + bo->placements[c++].flags = TTM_PL_MASK_CACHING + | TTM_PL_FLAG_SYSTEM; + } + for (i = 0; i < c; ++i) { + bo->placements[i].fpfn = 0; + bo->placements[i].lpfn = 0; } bo->placement.num_placement = c; bo->placement.num_busy_placement = c; @@ -294,7 +300,7 @@ int bochs_bo_pin(struct bochs_bo *bo, u32 pl_flag, u64 *gpu_addr) bochs_ttm_placement(bo, pl_flag); for (i = 0; i < bo->placement.num_placement; i++) - bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) return ret; @@ -319,7 +325,7 @@ int bochs_bo_unpin(struct bochs_bo *bo) return 0; for (i = 0; i < bo->placement.num_placement; i++) - bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT; + bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) return ret; @@ -333,7 +339,7 @@ int bochs_mmap(struct file *filp, struct vm_area_struct *vma) struct bochs_device *bochs; if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) - return drm_mmap(filp, vma); + return -EINVAL; file_priv = filp->private_data; bochs = file_priv->minor->dev->dev_private; @@ -371,7 +377,7 @@ static int bochs_bo_create(struct drm_device *dev, int size, int align, ret = ttm_bo_init(&bochs->ttm.bdev, &bochsbo->bo, size, ttm_bo_type_device, &bochsbo->placement, align >> PAGE_SHIFT, false, NULL, acc_size, - NULL, bochs_bo_ttm_destroy); + NULL, NULL, bochs_bo_ttm_destroy); if (ret) return ret; diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index 919c73b9444723eee3bfcfa71408c55e9cb9f631..e705335101a59ea0a703d9b699d8ea7af9eafcb5 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -128,6 +128,7 @@ static struct drm_driver driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM, .load = cirrus_driver_load, .unload = cirrus_driver_unload, + .set_busid = drm_pci_set_busid, .fops = &cirrus_driver_fops, .name = DRIVER_NAME, .desc = DRIVER_DESC, diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h index 401c890b6c6a817011923215706a9f2e259d4d71..d44e69daa23966c0e2f38332069a81cc24070d0a 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.h +++ b/drivers/gpu/drm/cirrus/cirrus_drv.h @@ -21,6 +21,8 @@ #include #include +#include + #define DRIVER_AUTHOR "Matthew Garrett" #define DRIVER_NAME "cirrus" @@ -163,7 +165,7 @@ struct cirrus_bo { struct ttm_placement placement; struct ttm_bo_kmap_obj kmap; struct drm_gem_object gem; - u32 placements[3]; + struct ttm_place placements[3]; int pin_count; }; #define gem_to_cirrus_bo(gobj) container_of((gobj), struct cirrus_bo, gem) diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 2a135f253e2930dd4d06da29b1a6a0da166969a6..d231b1c317afac94476e572b9560566311a38168 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -160,7 +160,8 @@ static int cirrusfb_create_object(struct cirrus_fbdev *afbdev, static int cirrusfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { - struct cirrus_fbdev *gfbdev = (struct cirrus_fbdev *)helper; + struct cirrus_fbdev *gfbdev = + container_of(helper, struct cirrus_fbdev, helper); struct drm_device *dev = gfbdev->helper.dev; struct cirrus_device *cdev = gfbdev->helper.dev->dev_private; struct fb_info *info; diff --git a/drivers/gpu/drm/cirrus/cirrus_ttm.c b/drivers/gpu/drm/cirrus/cirrus_ttm.c index 92e6b77860970097b30bb745859d7a020661eefa..dfffd528517a53b1783e8f5c22d1f622d1e7d537 100644 --- a/drivers/gpu/drm/cirrus/cirrus_ttm.c +++ b/drivers/gpu/drm/cirrus/cirrus_ttm.c @@ -298,18 +298,21 @@ void cirrus_mm_fini(struct cirrus_device *cirrus) void cirrus_ttm_placement(struct cirrus_bo *bo, int domain) { u32 c = 0; - bo->placement.fpfn = 0; - bo->placement.lpfn = 0; + unsigned i; bo->placement.placement = bo->placements; bo->placement.busy_placement = bo->placements; if (domain & TTM_PL_FLAG_VRAM) - bo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM; + bo->placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM; if (domain & TTM_PL_FLAG_SYSTEM) - bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + bo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; if (!c) - bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + bo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; bo->placement.num_placement = c; bo->placement.num_busy_placement = c; + for (i = 0; i < c; ++i) { + bo->placements[i].fpfn = 0; + bo->placements[i].lpfn = 0; + } } int cirrus_bo_create(struct drm_device *dev, int size, int align, @@ -340,7 +343,7 @@ int cirrus_bo_create(struct drm_device *dev, int size, int align, ret = ttm_bo_init(&cirrus->ttm.bdev, &cirrusbo->bo, size, ttm_bo_type_device, &cirrusbo->placement, align >> PAGE_SHIFT, false, NULL, acc_size, - NULL, cirrus_bo_ttm_destroy); + NULL, NULL, cirrus_bo_ttm_destroy); if (ret) return ret; @@ -365,7 +368,7 @@ int cirrus_bo_pin(struct cirrus_bo *bo, u32 pl_flag, u64 *gpu_addr) cirrus_ttm_placement(bo, pl_flag); for (i = 0; i < bo->placement.num_placement; i++) - bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) return ret; @@ -392,7 +395,7 @@ int cirrus_bo_push_sysram(struct cirrus_bo *bo) cirrus_ttm_placement(bo, TTM_PL_FLAG_SYSTEM); for (i = 0; i < bo->placement.num_placement ; i++) - bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) { @@ -408,7 +411,7 @@ int cirrus_mmap(struct file *filp, struct vm_area_struct *vma) struct cirrus_device *cirrus; if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) - return drm_mmap(filp, vma); + return -EINVAL; file_priv = filp->private_data; cirrus = file_priv->minor->dev->dev_private; diff --git a/drivers/gpu/drm/drm_agpsupport.c b/drivers/gpu/drm/drm_agpsupport.c index dde205cef384c08db92d114c16138c607ddc1da0..4b2b4aa5033ba1d6988f4f2c7dfc1ae605d82da5 100644 --- a/drivers/gpu/drm/drm_agpsupport.c +++ b/drivers/gpu/drm/drm_agpsupport.c @@ -34,6 +34,7 @@ #include #include #include +#include "drm_legacy.h" #if __OS_HAS_AGP diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index 3cedae12b3c1f845084894cd4c8f2fe819cb1070..fc8e8aaa34fb37f44e338bb237867078d84c020f 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -34,6 +34,13 @@ */ #include +#include "drm_internal.h" + +struct drm_magic_entry { + struct list_head head; + struct drm_hash_item hash_item; + struct drm_file *priv; +}; /** * Find the file with the given magic number. diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index 61acb8f6756d1f14caca006cd48e88ab8c6786c6..569064a00693eb2932186d72eddfda8b2aa74d5c 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -1,18 +1,13 @@ -/** - * \file drm_bufs.c - * Generic buffer template - * - * \author Rickard E. (Rik) Faith - * \author Gareth Hughes - */ - /* - * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com + * Legacy: Generic DRM Buffer Management * * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. * + * Author: Rickard E. (Rik) Faith + * Author: Gareth Hughes + * * 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 @@ -39,6 +34,7 @@ #include #include #include +#include "drm_legacy.h" static struct drm_map_list *drm_find_matching_map(struct drm_device *dev, struct drm_local_map *map) @@ -365,9 +361,9 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset, return 0; } -int drm_addmap(struct drm_device * dev, resource_size_t offset, - unsigned int size, enum drm_map_type type, - enum drm_map_flags flags, struct drm_local_map ** map_ptr) +int drm_legacy_addmap(struct drm_device * dev, resource_size_t offset, + unsigned int size, enum drm_map_type type, + enum drm_map_flags flags, struct drm_local_map **map_ptr) { struct drm_map_list *list; int rc; @@ -377,8 +373,7 @@ int drm_addmap(struct drm_device * dev, resource_size_t offset, *map_ptr = list->map; return rc; } - -EXPORT_SYMBOL(drm_addmap); +EXPORT_SYMBOL(drm_legacy_addmap); /** * Ioctl to specify a range of memory that is available for mapping by a @@ -391,8 +386,8 @@ EXPORT_SYMBOL(drm_addmap); * \return zero on success or a negative value on error. * */ -int drm_addmap_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) +int drm_legacy_addmap_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) { struct drm_map *map = data; struct drm_map_list *maplist; @@ -429,9 +424,9 @@ int drm_addmap_ioctl(struct drm_device *dev, void *data, * its being used, and free any associate resource (such as MTRR's) if it's not * being on use. * - * \sa drm_addmap + * \sa drm_legacy_addmap */ -int drm_rmmap_locked(struct drm_device *dev, struct drm_local_map *map) +int drm_legacy_rmmap_locked(struct drm_device *dev, struct drm_local_map *map) { struct drm_map_list *r_list = NULL, *list_t; drm_dma_handle_t dmah; @@ -478,26 +473,26 @@ int drm_rmmap_locked(struct drm_device *dev, struct drm_local_map *map) dmah.vaddr = map->handle; dmah.busaddr = map->offset; dmah.size = map->size; - __drm_pci_free(dev, &dmah); + __drm_legacy_pci_free(dev, &dmah); break; } kfree(map); return 0; } -EXPORT_SYMBOL(drm_rmmap_locked); +EXPORT_SYMBOL(drm_legacy_rmmap_locked); -int drm_rmmap(struct drm_device *dev, struct drm_local_map *map) +int drm_legacy_rmmap(struct drm_device *dev, struct drm_local_map *map) { int ret; mutex_lock(&dev->struct_mutex); - ret = drm_rmmap_locked(dev, map); + ret = drm_legacy_rmmap_locked(dev, map); mutex_unlock(&dev->struct_mutex); return ret; } -EXPORT_SYMBOL(drm_rmmap); +EXPORT_SYMBOL(drm_legacy_rmmap); /* The rmmap ioctl appears to be unnecessary. All mappings are torn down on * the last close of the device, and this is necessary for cleanup when things @@ -514,8 +509,8 @@ EXPORT_SYMBOL(drm_rmmap); * \param arg pointer to a struct drm_map structure. * \return zero on success or a negative value on error. */ -int drm_rmmap_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) +int drm_legacy_rmmap_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) { struct drm_map *request = data; struct drm_local_map *map = NULL; @@ -546,7 +541,7 @@ int drm_rmmap_ioctl(struct drm_device *dev, void *data, return 0; } - ret = drm_rmmap_locked(dev, map); + ret = drm_legacy_rmmap_locked(dev, map); mutex_unlock(&dev->struct_mutex); @@ -599,7 +594,8 @@ static void drm_cleanup_buf_error(struct drm_device * dev, * reallocates the buffer list of the same size order to accommodate the new * buffers. */ -int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request) +int drm_legacy_addbufs_agp(struct drm_device *dev, + struct drm_buf_desc *request) { struct drm_device_dma *dma = dev->dma; struct drm_buf_entry *entry; @@ -759,10 +755,11 @@ int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request) atomic_dec(&dev->buf_alloc); return 0; } -EXPORT_SYMBOL(drm_addbufs_agp); +EXPORT_SYMBOL(drm_legacy_addbufs_agp); #endif /* __OS_HAS_AGP */ -int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request) +int drm_legacy_addbufs_pci(struct drm_device *dev, + struct drm_buf_desc *request) { struct drm_device_dma *dma = dev->dma; int count; @@ -964,9 +961,10 @@ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request) return 0; } -EXPORT_SYMBOL(drm_addbufs_pci); +EXPORT_SYMBOL(drm_legacy_addbufs_pci); -static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request) +static int drm_legacy_addbufs_sg(struct drm_device *dev, + struct drm_buf_desc *request) { struct drm_device_dma *dma = dev->dma; struct drm_buf_entry *entry; @@ -1135,8 +1133,8 @@ static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request * addbufs_sg() or addbufs_pci() for AGP, scatter-gather or consistent * PCI memory respectively. */ -int drm_addbufs(struct drm_device *dev, void *data, - struct drm_file *file_priv) +int drm_legacy_addbufs(struct drm_device *dev, void *data, + struct drm_file *file_priv) { struct drm_buf_desc *request = data; int ret; @@ -1149,15 +1147,15 @@ int drm_addbufs(struct drm_device *dev, void *data, #if __OS_HAS_AGP if (request->flags & _DRM_AGP_BUFFER) - ret = drm_addbufs_agp(dev, request); + ret = drm_legacy_addbufs_agp(dev, request); else #endif if (request->flags & _DRM_SG_BUFFER) - ret = drm_addbufs_sg(dev, request); + ret = drm_legacy_addbufs_sg(dev, request); else if (request->flags & _DRM_FB_BUFFER) ret = -EINVAL; else - ret = drm_addbufs_pci(dev, request); + ret = drm_legacy_addbufs_pci(dev, request); return ret; } @@ -1179,8 +1177,8 @@ int drm_addbufs(struct drm_device *dev, void *data, * lock, preventing of allocating more buffers after this call. Information * about each requested buffer is then copied into user space. */ -int drm_infobufs(struct drm_device *dev, void *data, - struct drm_file *file_priv) +int drm_legacy_infobufs(struct drm_device *dev, void *data, + struct drm_file *file_priv) { struct drm_device_dma *dma = dev->dma; struct drm_buf_info *request = data; @@ -1260,8 +1258,8 @@ int drm_infobufs(struct drm_device *dev, void *data, * * \note This ioctl is deprecated and mostly never used. */ -int drm_markbufs(struct drm_device *dev, void *data, - struct drm_file *file_priv) +int drm_legacy_markbufs(struct drm_device *dev, void *data, + struct drm_file *file_priv) { struct drm_device_dma *dma = dev->dma; struct drm_buf_desc *request = data; @@ -1307,8 +1305,8 @@ int drm_markbufs(struct drm_device *dev, void *data, * Calls free_buffer() for each used buffer. * This function is primarily used for debugging. */ -int drm_freebufs(struct drm_device *dev, void *data, - struct drm_file *file_priv) +int drm_legacy_freebufs(struct drm_device *dev, void *data, + struct drm_file *file_priv) { struct drm_device_dma *dma = dev->dma; struct drm_buf_free *request = data; @@ -1340,7 +1338,7 @@ int drm_freebufs(struct drm_device *dev, void *data, task_pid_nr(current)); return -EINVAL; } - drm_free_buffer(dev, buf); + drm_legacy_free_buffer(dev, buf); } return 0; @@ -1360,8 +1358,8 @@ int drm_freebufs(struct drm_device *dev, void *data, * offset equal to 0, which drm_mmap() interpretes as PCI buffers and calls * drm_mmap_dma(). */ -int drm_mapbufs(struct drm_device *dev, void *data, - struct drm_file *file_priv) +int drm_legacy_mapbufs(struct drm_device *dev, void *data, + struct drm_file *file_priv) { struct drm_device_dma *dma = dev->dma; int retcode = 0; @@ -1448,7 +1446,7 @@ int drm_mapbufs(struct drm_device *dev, void *data, return retcode; } -int drm_dma_ioctl(struct drm_device *dev, void *data, +int drm_legacy_dma_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { if (drm_core_check_feature(dev, DRIVER_MODESET)) @@ -1460,7 +1458,7 @@ int drm_dma_ioctl(struct drm_device *dev, void *data, return -EINVAL; } -struct drm_local_map *drm_getsarea(struct drm_device *dev) +struct drm_local_map *drm_legacy_getsarea(struct drm_device *dev) { struct drm_map_list *entry; @@ -1472,4 +1470,4 @@ struct drm_local_map *drm_getsarea(struct drm_device *dev) } return NULL; } -EXPORT_SYMBOL(drm_getsarea); +EXPORT_SYMBOL(drm_legacy_getsarea); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 90e773019eac78f0a247f840eb5ada047a0b5573..e79c8d3700d838ff87f1095bcb92a773dbe36018 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -40,106 +40,12 @@ #include #include "drm_crtc_internal.h" +#include "drm_internal.h" static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, struct drm_mode_fb_cmd2 *r, struct drm_file *file_priv); -/** - * drm_modeset_lock_all - take all modeset locks - * @dev: drm device - * - * This function takes all modeset locks, suitable where a more fine-grained - * scheme isn't (yet) implemented. Locks must be dropped with - * drm_modeset_unlock_all. - */ -void drm_modeset_lock_all(struct drm_device *dev) -{ - struct drm_mode_config *config = &dev->mode_config; - struct drm_modeset_acquire_ctx *ctx; - int ret; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (WARN_ON(!ctx)) - return; - - mutex_lock(&config->mutex); - - drm_modeset_acquire_init(ctx, 0); - -retry: - ret = drm_modeset_lock(&config->connection_mutex, ctx); - if (ret) - goto fail; - ret = drm_modeset_lock_all_crtcs(dev, ctx); - if (ret) - goto fail; - - WARN_ON(config->acquire_ctx); - - /* now we hold the locks, so now that it is safe, stash the - * ctx for drm_modeset_unlock_all(): - */ - config->acquire_ctx = ctx; - - drm_warn_on_modeset_not_all_locked(dev); - - return; - -fail: - if (ret == -EDEADLK) { - drm_modeset_backoff(ctx); - goto retry; - } -} -EXPORT_SYMBOL(drm_modeset_lock_all); - -/** - * drm_modeset_unlock_all - drop all modeset locks - * @dev: device - * - * This function drop all modeset locks taken by drm_modeset_lock_all. - */ -void drm_modeset_unlock_all(struct drm_device *dev) -{ - struct drm_mode_config *config = &dev->mode_config; - struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx; - - if (WARN_ON(!ctx)) - return; - - config->acquire_ctx = NULL; - drm_modeset_drop_locks(ctx); - drm_modeset_acquire_fini(ctx); - - kfree(ctx); - - mutex_unlock(&dev->mode_config.mutex); -} -EXPORT_SYMBOL(drm_modeset_unlock_all); - -/** - * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked - * @dev: device - * - * Useful as a debug assert. - */ -void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) -{ - struct drm_crtc *crtc; - - /* Locking is currently fubar in the panic handler. */ - if (oops_in_progress) - return; - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); - - WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); - WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); -} -EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); - /* Avoid boilerplate. I'm tired of typing. */ #define DRM_ENUM_NAME_FN(fnname, list) \ const char *fnname(int val) \ @@ -515,9 +421,6 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, if (ret) goto out; - /* Grab the idr reference. */ - drm_framebuffer_reference(fb); - dev->mode_config.num_fb++; list_add(&fb->head, &dev->mode_config.fb_list); out: @@ -527,10 +430,34 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, } EXPORT_SYMBOL(drm_framebuffer_init); +/* dev->mode_config.fb_lock must be held! */ +static void __drm_framebuffer_unregister(struct drm_device *dev, + struct drm_framebuffer *fb) +{ + mutex_lock(&dev->mode_config.idr_mutex); + idr_remove(&dev->mode_config.crtc_idr, fb->base.id); + mutex_unlock(&dev->mode_config.idr_mutex); + + fb->base.id = 0; +} + static void drm_framebuffer_free(struct kref *kref) { struct drm_framebuffer *fb = container_of(kref, struct drm_framebuffer, refcount); + struct drm_device *dev = fb->dev; + + /* + * The lookup idr holds a weak reference, which has not necessarily been + * removed at this point. Check for that. + */ + mutex_lock(&dev->mode_config.fb_lock); + if (fb->base.id) { + /* Mark fb as reaped and drop idr ref. */ + __drm_framebuffer_unregister(dev, fb); + } + mutex_unlock(&dev->mode_config.fb_lock); + fb->funcs->destroy(fb); } @@ -567,8 +494,10 @@ struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, mutex_lock(&dev->mode_config.fb_lock); fb = __drm_framebuffer_lookup(dev, id); - if (fb) - drm_framebuffer_reference(fb); + if (fb) { + if (!kref_get_unless_zero(&fb->refcount)) + fb = NULL; + } mutex_unlock(&dev->mode_config.fb_lock); return fb; @@ -612,19 +541,6 @@ static void __drm_framebuffer_unreference(struct drm_framebuffer *fb) kref_put(&fb->refcount, drm_framebuffer_free_bug); } -/* dev->mode_config.fb_lock must be held! */ -static void __drm_framebuffer_unregister(struct drm_device *dev, - struct drm_framebuffer *fb) -{ - mutex_lock(&dev->mode_config.idr_mutex); - idr_remove(&dev->mode_config.crtc_idr, fb->base.id); - mutex_unlock(&dev->mode_config.idr_mutex); - - fb->base.id = 0; - - __drm_framebuffer_unreference(fb); -} - /** * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr * @fb: fb to unregister @@ -764,11 +680,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, crtc->funcs = funcs; crtc->invert_dimensions = false; - drm_modeset_lock_all(dev); drm_modeset_lock_init(&crtc->mutex); - /* dropped by _unlock_all(): */ - drm_modeset_lock(&crtc->mutex, config->acquire_ctx); - ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); if (ret) goto out; @@ -786,7 +698,6 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, cursor->possible_crtcs = 1 << drm_crtc_index(crtc); out: - drm_modeset_unlock_all(dev); return ret; } @@ -852,6 +763,59 @@ static void drm_mode_remove(struct drm_connector *connector, drm_mode_destroy(connector->dev, mode); } +/** + * drm_connector_get_cmdline_mode - reads the user's cmdline mode + * @connector: connector to quwery + * @mode: returned mode + * + * The kernel supports per-connector configration of its consoles through + * use of the video= parameter. This function parses that option and + * extracts the user's specified mode (or enable/disable status) for a + * particular connector. This is typically only used during the early fbdev + * setup. + */ +static void drm_connector_get_cmdline_mode(struct drm_connector *connector) +{ + struct drm_cmdline_mode *mode = &connector->cmdline_mode; + char *option = NULL; + + if (fb_get_options(connector->name, &option)) + return; + + if (!drm_mode_parse_command_line_for_connector(option, + connector, + mode)) + return; + + if (mode->force) { + const char *s; + + switch (mode->force) { + case DRM_FORCE_OFF: + s = "OFF"; + break; + case DRM_FORCE_ON_DIGITAL: + s = "ON - dig"; + break; + default: + case DRM_FORCE_ON: + s = "ON"; + break; + } + + DRM_INFO("forcing %s connector %s\n", connector->name, s); + connector->force = mode->force; + } + + DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", + connector->name, + mode->xres, mode->yres, + mode->refresh_specified ? mode->refresh : 60, + mode->rb ? " reduced blanking" : "", + mode->margins ? " with margins" : "", + mode->interlace ? " interlaced" : ""); +} + /** * drm_connector_init - Init a preallocated connector * @dev: DRM device @@ -904,6 +868,8 @@ int drm_connector_init(struct drm_device *dev, connector->edid_blob_ptr = NULL; connector->status = connector_status_unknown; + drm_connector_get_cmdline_mode(connector); + list_add_tail(&connector->head, &dev->mode_config.connector_list); dev->mode_config.num_connector++; @@ -956,6 +922,29 @@ void drm_connector_cleanup(struct drm_connector *connector) } EXPORT_SYMBOL(drm_connector_cleanup); +/** + * drm_connector_index - find the index of a registered connector + * @connector: connector to find index for + * + * Given a registered connector, return the index of that connector within a DRM + * device's list of connectors. + */ +unsigned int drm_connector_index(struct drm_connector *connector) +{ + unsigned int index = 0; + struct drm_connector *tmp; + + list_for_each_entry(tmp, &connector->dev->mode_config.connector_list, head) { + if (tmp == connector) + return index; + + index++; + } + + BUG(); +} +EXPORT_SYMBOL(drm_connector_index); + /** * drm_connector_register - register a connector * @connector: the connector to register @@ -1260,6 +1249,29 @@ void drm_plane_cleanup(struct drm_plane *plane) } EXPORT_SYMBOL(drm_plane_cleanup); +/** + * drm_plane_index - find the index of a registered plane + * @plane: plane to find index for + * + * Given a registered plane, return the index of that CRTC within a DRM + * device's list of planes. + */ +unsigned int drm_plane_index(struct drm_plane *plane) +{ + unsigned int index = 0; + struct drm_plane *tmp; + + list_for_each_entry(tmp, &plane->dev->mode_config.plane_list, head) { + if (tmp == plane) + return index; + + index++; + } + + BUG(); +} +EXPORT_SYMBOL(drm_plane_index); + /** * drm_plane_force_disable - Forcibly disable a plane * @plane: plane to disable @@ -1271,19 +1283,21 @@ EXPORT_SYMBOL(drm_plane_cleanup); */ void drm_plane_force_disable(struct drm_plane *plane) { - struct drm_framebuffer *old_fb = plane->fb; int ret; - if (!old_fb) + if (!plane->fb) return; + plane->old_fb = plane->fb; ret = plane->funcs->disable_plane(plane); if (ret) { DRM_ERROR("failed to disable plane with busy fb\n"); + plane->old_fb = NULL; return; } /* disconnect the plane from the fb and crtc: */ - __drm_framebuffer_unreference(old_fb); + __drm_framebuffer_unreference(plane->old_fb); + plane->old_fb = NULL; plane->fb = NULL; plane->crtc = NULL; } @@ -2249,33 +2263,29 @@ int drm_mode_getplane(struct drm_device *dev, void *data, * * src_{x,y,w,h} are provided in 16.16 fixed point format */ -static int setplane_internal(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, - int32_t crtc_x, int32_t crtc_y, - uint32_t crtc_w, uint32_t crtc_h, - /* src_{x,y,w,h} values are 16.16 fixed point */ - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) +static int __setplane_internal(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int32_t crtc_x, int32_t crtc_y, + uint32_t crtc_w, uint32_t crtc_h, + /* src_{x,y,w,h} values are 16.16 fixed point */ + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) { - struct drm_device *dev = plane->dev; - struct drm_framebuffer *old_fb = NULL; int ret = 0; unsigned int fb_width, fb_height; int i; /* No fb means shut it down */ if (!fb) { - drm_modeset_lock_all(dev); - old_fb = plane->fb; + plane->old_fb = plane->fb; ret = plane->funcs->disable_plane(plane); if (!ret) { plane->crtc = NULL; plane->fb = NULL; } else { - old_fb = NULL; + plane->old_fb = NULL; } - drm_modeset_unlock_all(dev); goto out; } @@ -2315,8 +2325,7 @@ static int setplane_internal(struct drm_plane *plane, goto out; } - drm_modeset_lock_all(dev); - old_fb = plane->fb; + plane->old_fb = plane->fb; ret = plane->funcs->update_plane(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, src_x, src_y, src_w, src_h); @@ -2325,18 +2334,37 @@ static int setplane_internal(struct drm_plane *plane, plane->fb = fb; fb = NULL; } else { - old_fb = NULL; + plane->old_fb = NULL; } - drm_modeset_unlock_all(dev); out: if (fb) drm_framebuffer_unreference(fb); - if (old_fb) - drm_framebuffer_unreference(old_fb); + if (plane->old_fb) + drm_framebuffer_unreference(plane->old_fb); + plane->old_fb = NULL; return ret; +} + +static int setplane_internal(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int32_t crtc_x, int32_t crtc_y, + uint32_t crtc_w, uint32_t crtc_h, + /* src_{x,y,w,h} values are 16.16 fixed point */ + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) +{ + int ret; + + drm_modeset_lock_all(plane->dev); + ret = __setplane_internal(plane, crtc, fb, + crtc_x, crtc_y, crtc_w, crtc_h, + src_x, src_y, src_w, src_h); + drm_modeset_unlock_all(plane->dev); + return ret; } /** @@ -2440,7 +2468,7 @@ int drm_mode_set_config_internal(struct drm_mode_set *set) * crtcs. Atomic modeset will have saner semantics ... */ list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) - tmp->old_fb = tmp->primary->fb; + tmp->primary->old_fb = tmp->primary->fb; fb = set->fb; @@ -2453,8 +2481,9 @@ int drm_mode_set_config_internal(struct drm_mode_set *set) list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) { if (tmp->primary->fb) drm_framebuffer_reference(tmp->primary->fb); - if (tmp->old_fb) - drm_framebuffer_unreference(tmp->old_fb); + if (tmp->primary->old_fb) + drm_framebuffer_unreference(tmp->primary->old_fb); + tmp->primary->old_fb = NULL; } return ret; @@ -2701,6 +2730,7 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc, int ret = 0; BUG_ON(!crtc->cursor); + WARN_ON(crtc->cursor->crtc != crtc && crtc->cursor->crtc != NULL); /* * Obtain fb we'll be using (either new or existing) and take an extra @@ -2720,11 +2750,9 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc, fb = NULL; } } else { - mutex_lock(&dev->mode_config.mutex); fb = crtc->cursor->fb; if (fb) drm_framebuffer_reference(fb); - mutex_unlock(&dev->mode_config.mutex); } if (req->flags & DRM_MODE_CURSOR_MOVE) { @@ -2746,7 +2774,7 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc, * setplane_internal will take care of deref'ing either the old or new * framebuffer depending on success. */ - ret = setplane_internal(crtc->cursor, crtc, fb, + ret = __setplane_internal(crtc->cursor, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, 0, 0, src_w, src_h); @@ -2782,10 +2810,12 @@ static int drm_mode_cursor_common(struct drm_device *dev, * If this crtc has a universal cursor plane, call that plane's update * handler rather than using legacy cursor handlers. */ - if (crtc->cursor) - return drm_mode_cursor_universal(crtc, req, file_priv); + drm_modeset_lock_crtc(crtc); + if (crtc->cursor) { + ret = drm_mode_cursor_universal(crtc, req, file_priv); + goto out; + } - drm_modeset_lock(&crtc->mutex, NULL); if (req->flags & DRM_MODE_CURSOR_BO) { if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { ret = -ENXIO; @@ -2809,7 +2839,7 @@ static int drm_mode_cursor_common(struct drm_device *dev, } } out: - drm_modeset_unlock(&crtc->mutex); + drm_modeset_unlock_crtc(crtc); return ret; @@ -3370,7 +3400,16 @@ void drm_fb_release(struct drm_file *priv) struct drm_device *dev = priv->minor->dev; struct drm_framebuffer *fb, *tfb; - mutex_lock(&priv->fbs_lock); + /* + * When the file gets released that means no one else can access the fb + * list any more, so no need to grab fpriv->fbs_lock. And we need to to + * avoid upsetting lockdep since the universal cursor code adds a + * framebuffer while holding mutex locks. + * + * Note that a real deadlock between fpriv->fbs_lock and the modeset + * locks is impossible here since no one else but this function can get + * at it any more. + */ list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { mutex_lock(&dev->mode_config.fb_lock); @@ -3383,7 +3422,6 @@ void drm_fb_release(struct drm_file *priv) /* This will also drop the fpriv->fbs reference. */ drm_framebuffer_remove(fb); } - mutex_unlock(&priv->fbs_lock); } /** @@ -3495,9 +3533,10 @@ EXPORT_SYMBOL(drm_property_create_enum); * @flags: flags specifying the property type * @name: name of the property * @props: enumeration lists with property bitflags - * @num_values: number of pre-defined values + * @num_props: size of the @props array + * @supported_bits: bitmask of all supported enumeration values * - * This creates a new generic drm property which can then be attached to a drm + * This creates a new bitmask drm property which can then be attached to a drm * object with drm_object_attach_property. The returned property object must be * freed with drm_property_destroy. * @@ -4157,12 +4196,25 @@ static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, return ret; } -static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj, - struct drm_property *property, - uint64_t value) +/** + * drm_mode_plane_set_obj_prop - set the value of a property + * @plane: drm plane object to set property value for + * @property: property to set + * @value: value the property should be set to + * + * This functions sets a given property on a given plane object. This function + * calls the driver's ->set_property callback and changes the software state of + * the property if the callback succeeds. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_mode_plane_set_obj_prop(struct drm_plane *plane, + struct drm_property *property, + uint64_t value) { int ret = -EINVAL; - struct drm_plane *plane = obj_to_plane(obj); + struct drm_mode_object *obj = &plane->base; if (plane->funcs->set_property) ret = plane->funcs->set_property(plane, property, value); @@ -4171,6 +4223,7 @@ static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj, return ret; } +EXPORT_SYMBOL(drm_mode_plane_set_obj_prop); /** * drm_mode_getproperty_ioctl - get the current value of a object's property @@ -4309,7 +4362,8 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value); break; case DRM_MODE_OBJECT_PLANE: - ret = drm_mode_plane_set_obj_prop(arg_obj, property, arg->value); + ret = drm_mode_plane_set_obj_prop(obj_to_plane(arg_obj), + property, arg->value); break; } @@ -4529,7 +4583,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, { struct drm_mode_crtc_page_flip *page_flip = data; struct drm_crtc *crtc; - struct drm_framebuffer *fb = NULL, *old_fb = NULL; + struct drm_framebuffer *fb = NULL; struct drm_pending_vblank_event *e = NULL; unsigned long flags; int ret = -EINVAL; @@ -4545,7 +4599,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (!crtc) return -ENOENT; - drm_modeset_lock(&crtc->mutex, NULL); + drm_modeset_lock_crtc(crtc); if (crtc->primary->fb == NULL) { /* The framebuffer is currently unbound, presumably * due to a hotplug event, that userspace has not @@ -4601,7 +4655,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, (void (*) (struct drm_pending_event *)) kfree; } - old_fb = crtc->primary->fb; + crtc->primary->old_fb = crtc->primary->fb; ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags); if (ret) { if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { @@ -4611,7 +4665,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, kfree(e); } /* Keep the old fb, don't unref it. */ - old_fb = NULL; + crtc->primary->old_fb = NULL; } else { /* * Warn if the driver hasn't properly updated the crtc->fb @@ -4627,9 +4681,10 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, out: if (fb) drm_framebuffer_unreference(fb); - if (old_fb) - drm_framebuffer_unreference(old_fb); - drm_modeset_unlock(&crtc->mutex); + if (crtc->primary->old_fb) + drm_framebuffer_unreference(crtc->primary->old_fb); + crtc->primary->old_fb = NULL; + drm_modeset_unlock_crtc(crtc); return ret; } @@ -4645,9 +4700,14 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, void drm_mode_config_reset(struct drm_device *dev) { struct drm_crtc *crtc; + struct drm_plane *plane; struct drm_encoder *encoder; struct drm_connector *connector; + list_for_each_entry(plane, &dev->mode_config.plane_list, head) + if (plane->funcs->reset) + plane->funcs->reset(plane); + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) if (crtc->funcs->reset) crtc->funcs->reset(crtc); diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 13bd42923dd4e2b4ca018e3ee2b1cb09a30b4677..3bcf8e6a85b355437e7aca009a3a205b9ba8f428 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -36,6 +36,7 @@ #include #include #include +#include "drm_internal.h" #if defined(CONFIG_DEBUG_FS) @@ -49,9 +50,7 @@ static const struct drm_info_list drm_debugfs_list[] = { {"clients", drm_clients_info, 0}, {"bufs", drm_bufs_info, 0}, {"gem_names", drm_gem_name_info, DRIVER_GEM}, -#if DRM_DEBUG_CODE {"vma", drm_vma_info, 0}, -#endif }; #define DRM_DEBUGFS_ENTRIES ARRAY_SIZE(drm_debugfs_list) diff --git a/drivers/gpu/drm/drm_dma.c b/drivers/gpu/drm/drm_dma.c index 8a140a953754de73d2a524bbaae8599bc5063984..ea481800ef565dfa3bd4852456f0f05efe60ac32 100644 --- a/drivers/gpu/drm/drm_dma.c +++ b/drivers/gpu/drm/drm_dma.c @@ -35,6 +35,7 @@ #include #include +#include "drm_legacy.h" /** * Initialize the DMA data. @@ -124,7 +125,7 @@ void drm_legacy_dma_takedown(struct drm_device *dev) * * Resets the fields of \p buf. */ -void drm_free_buffer(struct drm_device *dev, struct drm_buf * buf) +void drm_legacy_free_buffer(struct drm_device *dev, struct drm_buf * buf) { if (!buf) return; @@ -142,8 +143,8 @@ void drm_free_buffer(struct drm_device *dev, struct drm_buf * buf) * * Frees each buffer associated with \p file_priv not already on the hardware. */ -void drm_core_reclaim_buffers(struct drm_device *dev, - struct drm_file *file_priv) +void drm_legacy_reclaim_buffers(struct drm_device *dev, + struct drm_file *file_priv) { struct drm_device_dma *dma = dev->dma; int i; @@ -154,7 +155,7 @@ void drm_core_reclaim_buffers(struct drm_device *dev, if (dma->buflist[i]->file_priv == file_priv) { switch (dma->buflist[i]->list) { case DRM_LIST_NONE: - drm_free_buffer(dev, dma->buflist[i]); + drm_legacy_free_buffer(dev, dma->buflist[i]); break; case DRM_LIST_WAIT: dma->buflist[i]->list = DRM_LIST_RECLAIM; @@ -166,5 +167,3 @@ void drm_core_reclaim_buffers(struct drm_device *dev, } } } - -EXPORT_SYMBOL(drm_core_reclaim_buffers); diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index ac3c2738db94ca2278686bfef33eb81e236d4bda..070f913d2dba40eede7075f6e4b4b652e18dbdfa 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -682,7 +682,7 @@ static int build_allocate_payload(struct drm_dp_sideband_msg_tx *msg, int port_n static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_vcpi *vcpi) { - int ret; + int ret, vcpi_ret; mutex_lock(&mgr->payload_lock); ret = find_first_zero_bit(&mgr->payload_mask, mgr->max_payloads + 1); @@ -692,8 +692,16 @@ static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr, goto out_unlock; } + vcpi_ret = find_first_zero_bit(&mgr->vcpi_mask, mgr->max_payloads + 1); + if (vcpi_ret > mgr->max_payloads) { + ret = -EINVAL; + DRM_DEBUG_KMS("out of vcpi ids %d\n", ret); + goto out_unlock; + } + set_bit(ret, &mgr->payload_mask); - vcpi->vcpi = ret; + set_bit(vcpi_ret, &mgr->vcpi_mask); + vcpi->vcpi = vcpi_ret + 1; mgr->proposed_vcpis[ret - 1] = vcpi; out_unlock: mutex_unlock(&mgr->payload_lock); @@ -701,15 +709,23 @@ static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr, } static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr, - int id) + int vcpi) { - if (id == 0) + int i; + if (vcpi == 0) return; mutex_lock(&mgr->payload_lock); - DRM_DEBUG_KMS("putting payload %d\n", id); - clear_bit(id, &mgr->payload_mask); - mgr->proposed_vcpis[id - 1] = NULL; + DRM_DEBUG_KMS("putting payload %d\n", vcpi); + clear_bit(vcpi - 1, &mgr->vcpi_mask); + + for (i = 0; i < mgr->max_payloads; i++) { + if (mgr->proposed_vcpis[i]) + if (mgr->proposed_vcpis[i]->vcpi == vcpi) { + mgr->proposed_vcpis[i] = NULL; + clear_bit(i + 1, &mgr->payload_mask); + } + } mutex_unlock(&mgr->payload_lock); } @@ -1563,7 +1579,7 @@ static int drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr *mgr, } drm_dp_dpcd_write_payload(mgr, id, payload); - payload->payload_state = 0; + payload->payload_state = DP_PAYLOAD_DELETE_LOCAL; return 0; } @@ -1590,7 +1606,7 @@ static int drm_dp_destroy_payload_step2(struct drm_dp_mst_topology_mgr *mgr, */ int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr) { - int i; + int i, j; int cur_slots = 1; struct drm_dp_payload req_payload; struct drm_dp_mst_port *port; @@ -1607,26 +1623,46 @@ int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr) port = NULL; req_payload.num_slots = 0; } + + if (mgr->payloads[i].start_slot != req_payload.start_slot) { + mgr->payloads[i].start_slot = req_payload.start_slot; + } /* work out what is required to happen with this payload */ - if (mgr->payloads[i].start_slot != req_payload.start_slot || - mgr->payloads[i].num_slots != req_payload.num_slots) { + if (mgr->payloads[i].num_slots != req_payload.num_slots) { /* need to push an update for this payload */ if (req_payload.num_slots) { - drm_dp_create_payload_step1(mgr, i + 1, &req_payload); + drm_dp_create_payload_step1(mgr, mgr->proposed_vcpis[i]->vcpi, &req_payload); mgr->payloads[i].num_slots = req_payload.num_slots; } else if (mgr->payloads[i].num_slots) { mgr->payloads[i].num_slots = 0; - drm_dp_destroy_payload_step1(mgr, port, i + 1, &mgr->payloads[i]); + drm_dp_destroy_payload_step1(mgr, port, port->vcpi.vcpi, &mgr->payloads[i]); req_payload.payload_state = mgr->payloads[i].payload_state; - } else - req_payload.payload_state = 0; - - mgr->payloads[i].start_slot = req_payload.start_slot; + mgr->payloads[i].start_slot = 0; + } mgr->payloads[i].payload_state = req_payload.payload_state; } cur_slots += req_payload.num_slots; } + + for (i = 0; i < mgr->max_payloads; i++) { + if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) { + DRM_DEBUG_KMS("removing payload %d\n", i); + for (j = i; j < mgr->max_payloads - 1; j++) { + memcpy(&mgr->payloads[j], &mgr->payloads[j + 1], sizeof(struct drm_dp_payload)); + mgr->proposed_vcpis[j] = mgr->proposed_vcpis[j + 1]; + if (mgr->proposed_vcpis[j] && mgr->proposed_vcpis[j]->num_slots) { + set_bit(j + 1, &mgr->payload_mask); + } else { + clear_bit(j + 1, &mgr->payload_mask); + } + } + memset(&mgr->payloads[mgr->max_payloads - 1], 0, sizeof(struct drm_dp_payload)); + mgr->proposed_vcpis[mgr->max_payloads - 1] = NULL; + clear_bit(mgr->max_payloads, &mgr->payload_mask); + + } + } mutex_unlock(&mgr->payload_lock); return 0; @@ -1657,9 +1693,9 @@ int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr) DRM_DEBUG_KMS("payload %d %d\n", i, mgr->payloads[i].payload_state); if (mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL) { - ret = drm_dp_create_payload_step2(mgr, port, i + 1, &mgr->payloads[i]); + ret = drm_dp_create_payload_step2(mgr, port, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]); } else if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) { - ret = drm_dp_destroy_payload_step2(mgr, i + 1, &mgr->payloads[i]); + ret = drm_dp_destroy_payload_step2(mgr, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]); } if (ret) { mutex_unlock(&mgr->payload_lock); @@ -1772,7 +1808,7 @@ static int drm_dp_get_vc_payload_bw(int dp_link_bw, int dp_link_count) case DP_LINK_BW_5_4: return 10 * dp_link_count; } - return 0; + BUG(); } /** @@ -1861,6 +1897,7 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms memset(mgr->payloads, 0, mgr->max_payloads * sizeof(struct drm_dp_payload)); mgr->payload_mask = 0; set_bit(0, &mgr->payload_mask); + mgr->vcpi_mask = 0; } out_unlock: @@ -2071,6 +2108,7 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr) * drm_dp_mst_hpd_irq() - MST hotplug IRQ notify * @mgr: manager to notify irq for. * @esi: 4 bytes from SINK_COUNT_ESI + * @handled: whether the hpd interrupt was consumed or not * * This should be called from the driver when it detects a short IRQ, * along with the value of the DEVICE_SERVICE_IRQ_VECTOR_ESI0. The @@ -2474,7 +2512,7 @@ void drm_dp_mst_dump_topology(struct seq_file *m, mutex_unlock(&mgr->lock); mutex_lock(&mgr->payload_lock); - seq_printf(m, "vcpi: %lx\n", mgr->payload_mask); + seq_printf(m, "vcpi: %lx %lx\n", mgr->payload_mask, mgr->vcpi_mask); for (i = 0; i < mgr->max_payloads; i++) { if (mgr->proposed_vcpis[i]) { diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 3242e208c0d0db205498df5e1ac371d30c488988..bc3da32d458590a849a785aeca890670741e5212 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -35,32 +35,20 @@ #include #include #include "drm_legacy.h" +#include "drm_internal.h" unsigned int drm_debug = 0; /* 1 to enable debug output */ EXPORT_SYMBOL(drm_debug); -unsigned int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */ - -unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ - -/* - * Default to use monotonic timestamps for wait-for-vblank and page-flip - * complete events. - */ -unsigned int drm_timestamp_monotonic = 1; - MODULE_AUTHOR(CORE_AUTHOR); MODULE_DESCRIPTION(CORE_DESC); MODULE_LICENSE("GPL and additional rights"); MODULE_PARM_DESC(debug, "Enable debug output"); -MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]"); +MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)"); MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]"); MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps"); module_param_named(debug, drm_debug, int, 0600); -module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); -module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); -module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); static DEFINE_SPINLOCK(drm_minor_lock); static struct idr drm_minors_idr; @@ -68,22 +56,19 @@ static struct idr drm_minors_idr; struct class *drm_class; static struct dentry *drm_debugfs_root; -int drm_err(const char *func, const char *format, ...) +void drm_err(const char *func, const char *format, ...) { struct va_format vaf; va_list args; - int r; va_start(args, format); vaf.fmt = format; vaf.va = &args; - r = printk(KERN_ERR "[" DRM_NAME ":%s] *ERROR* %pV", func, &vaf); + printk(KERN_ERR "[" DRM_NAME ":%s] *ERROR* %pV", func, &vaf); va_end(args); - - return r; } EXPORT_SYMBOL(drm_err); @@ -102,6 +87,8 @@ void drm_ut_debug_printk(const char *function_name, const char *format, ...) } EXPORT_SYMBOL(drm_ut_debug_printk); +#define DRM_MAGIC_HASH_ORDER 4 /**< Size of key hash table. Must be power of 2. */ + struct drm_master *drm_master_create(struct drm_minor *minor) { struct drm_master *master; @@ -133,7 +120,6 @@ EXPORT_SYMBOL(drm_master_get); static void drm_master_destroy(struct kref *kref) { struct drm_master *master = container_of(kref, struct drm_master, refcount); - struct drm_magic_entry *pt, *next; struct drm_device *dev = master->minor->dev; struct drm_map_list *r_list, *list_temp; @@ -143,7 +129,7 @@ static void drm_master_destroy(struct kref *kref) list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) { if (r_list->master == master) { - drm_rmmap_locked(dev, r_list->map); + drm_legacy_rmmap_locked(dev, r_list->map); r_list = NULL; } } @@ -154,12 +140,6 @@ static void drm_master_destroy(struct kref *kref) master->unique_len = 0; } - list_for_each_entry_safe(pt, next, &master->magicfree, head) { - list_del(&pt->head); - drm_ht_remove_item(&master->magiclist, &pt->hash_item); - kfree(pt); - } - drm_ht_remove(&master->magiclist); mutex_unlock(&dev->struct_mutex); @@ -615,7 +595,7 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver, goto err_ht; } - if (driver->driver_features & DRIVER_GEM) { + if (drm_core_check_feature(dev, DRIVER_GEM)) { ret = drm_gem_init(dev); if (ret) { DRM_ERROR("Cannot initialize graphics execution manager (GEM)\n"); @@ -645,7 +625,7 @@ static void drm_dev_release(struct kref *ref) { struct drm_device *dev = container_of(ref, struct drm_device, ref); - if (dev->driver->driver_features & DRIVER_GEM) + if (drm_core_check_feature(dev, DRIVER_GEM)) drm_gem_destroy(dev); drm_legacy_ctxbitmap_cleanup(dev); @@ -779,7 +759,7 @@ void drm_dev_unregister(struct drm_device *dev) drm_vblank_cleanup(dev); list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) - drm_rmmap(dev, r_list->map); + drm_legacy_rmmap(dev, r_list->map); drm_minor_unregister(dev, DRM_MINOR_LEGACY); drm_minor_unregister(dev, DRM_MINOR_RENDER); diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 1dbf3bc4c6a3cdd580d00706052976aed01b94c9..3bf999134bcc503dd12e9c8dce3244a81e447a24 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -632,27 +632,27 @@ static const struct drm_display_mode edid_cea_modes[] = { DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE), .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 6 - 1440x480i@60Hz */ - { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, - 1602, 1716, 0, 480, 488, 494, 525, 0, + /* 6 - 720(1440)x480i@60Hz */ + { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, + 801, 858, 0, 480, 488, 494, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 7 - 1440x480i@60Hz */ - { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, - 1602, 1716, 0, 480, 488, 494, 525, 0, + /* 7 - 720(1440)x480i@60Hz */ + { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, + 801, 858, 0, 480, 488, 494, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 8 - 1440x240@60Hz */ - { DRM_MODE("1440x240", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, - 1602, 1716, 0, 240, 244, 247, 262, 0, + /* 8 - 720(1440)x240@60Hz */ + { DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, + 801, 858, 0, 240, 244, 247, 262, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_DBLCLK), .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 9 - 1440x240@60Hz */ - { DRM_MODE("1440x240", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, - 1602, 1716, 0, 240, 244, 247, 262, 0, + /* 9 - 720(1440)x240@60Hz */ + { DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739, + 801, 858, 0, 240, 244, 247, 262, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_DBLCLK), .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, @@ -714,27 +714,27 @@ static const struct drm_display_mode edid_cea_modes[] = { DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE), .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 21 - 1440x576i@50Hz */ - { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, - 1590, 1728, 0, 576, 580, 586, 625, 0, + /* 21 - 720(1440)x576i@50Hz */ + { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, + 795, 864, 0, 576, 580, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 22 - 1440x576i@50Hz */ - { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, - 1590, 1728, 0, 576, 580, 586, 625, 0, + /* 22 - 720(1440)x576i@50Hz */ + { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, + 795, 864, 0, 576, 580, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 23 - 1440x288@50Hz */ - { DRM_MODE("1440x288", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, - 1590, 1728, 0, 288, 290, 293, 312, 0, + /* 23 - 720(1440)x288@50Hz */ + { DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, + 795, 864, 0, 288, 290, 293, 312, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_DBLCLK), .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 24 - 1440x288@50Hz */ - { DRM_MODE("1440x288", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, - 1590, 1728, 0, 288, 290, 293, 312, 0, + /* 24 - 720(1440)x288@50Hz */ + { DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732, + 795, 864, 0, 288, 290, 293, 312, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_DBLCLK), .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, @@ -837,17 +837,17 @@ static const struct drm_display_mode edid_cea_modes[] = { 796, 864, 0, 576, 581, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 44 - 1440x576i@100Hz */ - { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464, - 1590, 1728, 0, 576, 580, 586, 625, 0, + /* 44 - 720(1440)x576i@100Hz */ + { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, + 795, 864, 0, 576, 580, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_DBLCLK), + DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 45 - 1440x576i@100Hz */ - { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464, - 1590, 1728, 0, 576, 580, 586, 625, 0, + /* 45 - 720(1440)x576i@100Hz */ + { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732, + 795, 864, 0, 576, 580, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | - DRM_MODE_FLAG_DBLCLK), + DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 46 - 1920x1080i@120Hz */ { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, @@ -870,15 +870,15 @@ static const struct drm_display_mode edid_cea_modes[] = { 798, 858, 0, 480, 489, 495, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 50 - 1440x480i@120Hz */ - { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478, - 1602, 1716, 0, 480, 488, 494, 525, 0, + /* 50 - 720(1440)x480i@120Hz */ + { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739, + 801, 858, 0, 480, 488, 494, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 51 - 1440x480i@120Hz */ - { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478, - 1602, 1716, 0, 480, 488, 494, 525, 0, + /* 51 - 720(1440)x480i@120Hz */ + { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739, + 801, 858, 0, 480, 488, 494, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, @@ -892,15 +892,15 @@ static const struct drm_display_mode edid_cea_modes[] = { 796, 864, 0, 576, 581, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 54 - 1440x576i@200Hz */ - { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464, - 1590, 1728, 0, 576, 580, 586, 625, 0, + /* 54 - 720(1440)x576i@200Hz */ + { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, + 795, 864, 0, 576, 580, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 55 - 1440x576i@200Hz */ - { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464, - 1590, 1728, 0, 576, 580, 586, 625, 0, + /* 55 - 720(1440)x576i@200Hz */ + { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732, + 795, 864, 0, 576, 580, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, @@ -914,15 +914,15 @@ static const struct drm_display_mode edid_cea_modes[] = { 798, 858, 0, 480, 489, 495, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, - /* 58 - 1440x480i@240 */ - { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478, - 1602, 1716, 0, 480, 488, 494, 525, 0, + /* 58 - 720(1440)x480i@240 */ + { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739, + 801, 858, 0, 480, 488, 494, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, }, - /* 59 - 1440x480i@240 */ - { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478, - 1602, 1716, 0, 480, 488, 494, 525, 0, + /* 59 - 720(1440)x480i@240 */ + { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739, + 801, 858, 0, 480, 488, 494, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, @@ -2103,7 +2103,8 @@ static int add_inferred_modes(struct drm_connector *connector, struct edid *edid) { struct detailed_mode_closure closure = { - connector, edid, 0, 0, 0 + .connector = connector, + .edid = edid, }; if (version_greater(edid, 1, 0)) @@ -2169,7 +2170,8 @@ add_established_modes(struct drm_connector *connector, struct edid *edid) ((edid->established_timings.mfg_rsvd & 0x80) << 9); int i, modes = 0; struct detailed_mode_closure closure = { - connector, edid, 0, 0, 0 + .connector = connector, + .edid = edid, }; for (i = 0; i <= EDID_EST_TIMINGS; i++) { @@ -2227,7 +2229,8 @@ add_standard_modes(struct drm_connector *connector, struct edid *edid) { int i, modes = 0; struct detailed_mode_closure closure = { - connector, edid, 0, 0, 0 + .connector = connector, + .edid = edid, }; for (i = 0; i < EDID_STD_TIMINGS; i++) { @@ -2313,7 +2316,8 @@ static int add_cvt_modes(struct drm_connector *connector, struct edid *edid) { struct detailed_mode_closure closure = { - connector, edid, 0, 0, 0 + .connector = connector, + .edid = edid, }; if (version_greater(edid, 1, 2)) @@ -2357,11 +2361,10 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, u32 quirks) { struct detailed_mode_closure closure = { - connector, - edid, - 1, - quirks, - 0 + .connector = connector, + .edid = edid, + .preferred = 1, + .quirks = quirks, }; if (closure.preferred && !version_greater(edid, 1, 3)) @@ -3433,10 +3436,10 @@ EXPORT_SYMBOL(drm_rgb_quant_range_selectable); /** * drm_assign_hdmi_deep_color_info - detect whether monitor supports * hdmi deep color modes and update drm_display_info if so. - * * @edid: monitor EDID information * @info: Updated with maximum supported deep color bpc and color format * if deep color supported. + * @connector: DRM connector, used only for debug output * * Parse the CEA extension according to CEA-861-B. * Return true if HDMI deep color supported, false if not or unknown. diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 3144db9dc0f1f0bf3b0ea7ca8d42665fb47a37f6..0c0c39bac23da21a368ef94681da10b173994660 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -126,7 +126,7 @@ int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_ WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex)); if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) { - temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector) * (fb_helper->connector_count + 1), GFP_KERNEL); + temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector *) * (fb_helper->connector_count + 1), GFP_KERNEL); if (!temp) return -ENOMEM; @@ -171,60 +171,6 @@ int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, } EXPORT_SYMBOL(drm_fb_helper_remove_one_connector); -static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper) -{ - struct drm_fb_helper_connector *fb_helper_conn; - int i; - - for (i = 0; i < fb_helper->connector_count; i++) { - struct drm_cmdline_mode *mode; - struct drm_connector *connector; - char *option = NULL; - - fb_helper_conn = fb_helper->connector_info[i]; - connector = fb_helper_conn->connector; - mode = &fb_helper_conn->cmdline_mode; - - /* do something on return - turn off connector maybe */ - if (fb_get_options(connector->name, &option)) - continue; - - if (drm_mode_parse_command_line_for_connector(option, - connector, - mode)) { - if (mode->force) { - const char *s; - switch (mode->force) { - case DRM_FORCE_OFF: - s = "OFF"; - break; - case DRM_FORCE_ON_DIGITAL: - s = "ON - dig"; - break; - default: - case DRM_FORCE_ON: - s = "ON"; - break; - } - - DRM_INFO("forcing %s connector %s\n", - connector->name, s); - connector->force = mode->force; - } - - DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", - connector->name, - mode->xres, mode->yres, - mode->refresh_specified ? mode->refresh : 60, - mode->rb ? " reduced blanking" : "", - mode->margins ? " with margins" : "", - mode->interlace ? " interlaced" : ""); - } - - } - return 0; -} - static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper) { uint16_t *r_base, *g_base, *b_base; @@ -345,10 +291,17 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper) drm_warn_on_modeset_not_all_locked(dev); - list_for_each_entry(plane, &dev->mode_config.plane_list, head) + list_for_each_entry(plane, &dev->mode_config.plane_list, head) { if (plane->type != DRM_PLANE_TYPE_PRIMARY) drm_plane_force_disable(plane); + if (dev->mode_config.rotation_property) { + drm_mode_plane_set_obj_prop(plane, + dev->mode_config.rotation_property, + BIT(DRM_ROTATE_0)); + } + } + for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; struct drm_crtc *crtc = mode_set->crtc; @@ -419,11 +372,11 @@ static bool drm_fb_helper_force_kernel_mode(void) if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) continue; - /* NOTE: we use lockless flag below to avoid grabbing other - * modeset locks. So just trylock the underlying mutex - * directly: + /* + * NOTE: Use trylock mode to avoid deadlocks and sleeping in + * panic context. */ - if (!mutex_trylock(&dev->mode_config.mutex)) { + if (__drm_modeset_lock_all(dev, true) != 0) { error = true; continue; } @@ -432,7 +385,7 @@ static bool drm_fb_helper_force_kernel_mode(void) if (ret) error = true; - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); } return error; } @@ -1013,7 +966,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i]; struct drm_cmdline_mode *cmdline_mode; - cmdline_mode = &fb_helper_conn->cmdline_mode; + cmdline_mode = &fb_helper_conn->connector->cmdline_mode; if (cmdline_mode->bpp_specified) { switch (cmdline_mode->bpp) { @@ -1260,9 +1213,7 @@ EXPORT_SYMBOL(drm_has_preferred_mode); static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) { - struct drm_cmdline_mode *cmdline_mode; - cmdline_mode = &fb_connector->cmdline_mode; - return cmdline_mode->specified; + return fb_connector->connector->cmdline_mode.specified; } struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, @@ -1272,7 +1223,7 @@ struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *f struct drm_display_mode *mode = NULL; bool prefer_non_interlace; - cmdline_mode = &fb_helper_conn->cmdline_mode; + cmdline_mode = &fb_helper_conn->connector->cmdline_mode; if (cmdline_mode->specified == false) return mode; @@ -1657,8 +1608,6 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) struct drm_device *dev = fb_helper->dev; int count = 0; - drm_fb_helper_parse_command_line(fb_helper); - mutex_lock(&dev->mode_config.mutex); count = drm_fb_helper_probe_connector_modes(fb_helper, dev->mode_config.max_width, diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 79d5221c6e41c9880b5620623c9653799c1044ed..ed7bc68f7e8700041894c7f35685af449c4deb2e 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -39,10 +39,10 @@ #include #include #include "drm_legacy.h" +#include "drm_internal.h" /* from BKL pushdown */ DEFINE_MUTEX(drm_global_mutex); -EXPORT_SYMBOL(drm_global_mutex); static int drm_open_helper(struct file *filp, struct drm_minor *minor); @@ -171,7 +171,7 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) init_waitqueue_head(&priv->event_wait); priv->event_space = 4096; /* set aside 4k for event buffer */ - if (dev->driver->driver_features & DRIVER_GEM) + if (drm_core_check_feature(dev, DRIVER_GEM)) drm_gem_open(dev, priv); if (drm_core_check_feature(dev, DRIVER_PRIME)) @@ -256,7 +256,7 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) out_prime_destroy: if (drm_core_check_feature(dev, DRIVER_PRIME)) drm_prime_destroy_file_private(&priv->prime); - if (dev->driver->driver_features & DRIVER_GEM) + if (drm_core_check_feature(dev, DRIVER_GEM)) drm_gem_release(dev, priv); put_pid(priv->pid); kfree(priv); @@ -268,11 +268,11 @@ static void drm_master_release(struct drm_device *dev, struct file *filp) { struct drm_file *file_priv = filp->private_data; - if (drm_i_have_hw_lock(dev, file_priv)) { + if (drm_legacy_i_have_hw_lock(dev, file_priv)) { DRM_DEBUG("File %p released, freeing lock for context %d\n", filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); - drm_lock_free(&file_priv->master->lock, - _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); + drm_legacy_lock_free(&file_priv->master->lock, + _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); } } @@ -330,8 +330,6 @@ static void drm_legacy_dev_reinit(struct drm_device *dev) */ int drm_lastclose(struct drm_device * dev) { - struct drm_vma_entry *vma, *vma_temp; - DRM_DEBUG("\n"); if (dev->driver->lastclose) @@ -346,13 +344,7 @@ int drm_lastclose(struct drm_device * dev) drm_agp_clear(dev); drm_legacy_sg_cleanup(dev); - - /* Clear vma list (only built for debugging) */ - list_for_each_entry_safe(vma, vma_temp, &dev->vmalist, head) { - list_del(&vma->head); - kfree(vma); - } - + drm_legacy_vma_flush(dev); drm_legacy_dma_takedown(dev); mutex_unlock(&dev->struct_mutex); @@ -412,14 +404,14 @@ int drm_release(struct inode *inode, struct file *filp) drm_master_release(dev, filp); if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) - drm_core_reclaim_buffers(dev, file_priv); + drm_legacy_reclaim_buffers(dev, file_priv); drm_events_release(file_priv); - if (dev->driver->driver_features & DRIVER_MODESET) + if (drm_core_check_feature(dev, DRIVER_MODESET)) drm_fb_release(file_priv); - if (dev->driver->driver_features & DRIVER_GEM) + if (drm_core_check_feature(dev, DRIVER_GEM)) drm_gem_release(dev, file_priv); drm_legacy_ctxbitmap_flush(dev, file_priv); @@ -464,6 +456,8 @@ int drm_release(struct inode *inode, struct file *filp) if (drm_core_check_feature(dev, DRIVER_PRIME)) drm_prime_destroy_file_private(&file_priv->prime); + WARN_ON(!list_empty(&file_priv->event_list)); + put_pid(file_priv->pid); kfree(file_priv); diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 6adee4c2afc0870c6fb3bcbb07387f923d32eb1d..f6ca51259fa3a90dbc46fff70d8e15dc5e483ba8 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -38,6 +38,8 @@ #include #include #include +#include +#include "drm_internal.h" /** @file drm_gem.c * @@ -146,7 +148,7 @@ int drm_gem_object_init(struct drm_device *dev, EXPORT_SYMBOL(drm_gem_object_init); /** - * drm_gem_object_init - initialize an allocated private GEM object + * drm_gem_private_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 @@ -579,7 +581,7 @@ drm_gem_close_ioctl(struct drm_device *dev, void *data, struct drm_gem_close *args = data; int ret; - if (!(dev->driver->driver_features & DRIVER_GEM)) + if (!drm_core_check_feature(dev, DRIVER_GEM)) return -ENODEV; ret = drm_gem_handle_delete(file_priv, args->handle); @@ -606,7 +608,7 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data, struct drm_gem_object *obj; int ret; - if (!(dev->driver->driver_features & DRIVER_GEM)) + if (!drm_core_check_feature(dev, DRIVER_GEM)) return -ENODEV; obj = drm_gem_object_lookup(dev, file_priv, args->handle); @@ -659,7 +661,7 @@ drm_gem_open_ioctl(struct drm_device *dev, void *data, int ret; u32 handle; - if (!(dev->driver->driver_features & DRIVER_GEM)) + if (!drm_core_check_feature(dev, DRIVER_GEM)) return -ENODEV; mutex_lock(&dev->object_name_lock); @@ -887,7 +889,7 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) vma_pages(vma)); if (!node) { mutex_unlock(&dev->struct_mutex); - return drm_mmap(filp, vma); + return -EINVAL; } else if (!drm_vma_node_is_allowed(node, filp)) { mutex_unlock(&dev->struct_mutex); return -EACCES; diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index e467e67af6e7111c69b96cb792380b5727500650..0316310e2cc47400703418ea324e381cc65f8c8b 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -316,7 +316,8 @@ struct sg_table *drm_gem_cma_prime_get_sg_table(struct drm_gem_object *obj) EXPORT_SYMBOL_GPL(drm_gem_cma_prime_get_sg_table); struct drm_gem_object * -drm_gem_cma_prime_import_sg_table(struct drm_device *dev, size_t size, +drm_gem_cma_prime_import_sg_table(struct drm_device *dev, + struct dma_buf_attachment *attach, struct sg_table *sgt) { struct drm_gem_cma_object *cma_obj; @@ -325,14 +326,14 @@ drm_gem_cma_prime_import_sg_table(struct drm_device *dev, size_t size, return ERR_PTR(-EINVAL); /* Create a CMA GEM buffer. */ - cma_obj = __drm_gem_cma_create(dev, size); + cma_obj = __drm_gem_cma_create(dev, attach->dmabuf->size); if (IS_ERR(cma_obj)) return ERR_CAST(cma_obj); cma_obj->paddr = sg_dma_address(sgt->sgl); cma_obj->sgt = sgt; - DRM_DEBUG_PRIME("dma_addr = %pad, size = %zu\n", &cma_obj->paddr, size); + DRM_DEBUG_PRIME("dma_addr = %pad, size = %zu\n", &cma_obj->paddr, attach->dmabuf->size); return &cma_obj->base; } diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c index ecaf0fa2eec8fe5d3c95ea324b6d6e0510003dec..51efebd434f302d61134c2eb384935ef4c1b2521 100644 --- a/drivers/gpu/drm/drm_info.c +++ b/drivers/gpu/drm/drm_info.c @@ -35,6 +35,9 @@ #include #include +#include + +#include "drm_legacy.h" /** * Called when "/proc/dri/.../name" is read. @@ -183,15 +186,32 @@ int drm_clients_info(struct seq_file *m, void *data) struct drm_device *dev = node->minor->dev; struct drm_file *priv; + seq_printf(m, + "%20s %5s %3s master a %5s %10s\n", + "command", + "pid", + "dev", + "uid", + "magic"); + + /* dev->filelist is sorted youngest first, but we want to present + * oldest first (i.e. kernel, servers, clients), so walk backwardss. + */ mutex_lock(&dev->struct_mutex); - seq_printf(m, "a dev pid uid magic\n\n"); - list_for_each_entry(priv, &dev->filelist, lhead) { - seq_printf(m, "%c %3d %5d %5d %10u\n", - priv->authenticated ? 'y' : 'n', - priv->minor->index, + list_for_each_entry_reverse(priv, &dev->filelist, lhead) { + struct task_struct *task; + + rcu_read_lock(); /* locks pid_task()->comm */ + task = pid_task(priv->pid, PIDTYPE_PID); + seq_printf(m, "%20s %5d %3d %c %c %5d %10u\n", + task ? task->comm : "", pid_vnr(priv->pid), + priv->minor->index, + priv->is_master ? 'y' : 'n', + priv->authenticated ? 'y' : 'n', from_kuid_munged(seq_user_ns(m), priv->uid), priv->magic); + rcu_read_unlock(); } mutex_unlock(&dev->struct_mutex); return 0; @@ -223,62 +243,3 @@ int drm_gem_name_info(struct seq_file *m, void *data) return 0; } - -#if DRM_DEBUG_CODE - -int drm_vma_info(struct seq_file *m, void *data) -{ - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - struct drm_vma_entry *pt; - struct vm_area_struct *vma; - unsigned long vma_count = 0; -#if defined(__i386__) - unsigned int pgprot; -#endif - - mutex_lock(&dev->struct_mutex); - list_for_each_entry(pt, &dev->vmalist, head) - vma_count++; - - seq_printf(m, "vma use count: %lu, high_memory = %pK, 0x%pK\n", - vma_count, high_memory, - (void *)(unsigned long)virt_to_phys(high_memory)); - - list_for_each_entry(pt, &dev->vmalist, head) { - vma = pt->vma; - if (!vma) - continue; - seq_printf(m, - "\n%5d 0x%pK-0x%pK %c%c%c%c%c%c 0x%08lx000", - pt->pid, - (void *)vma->vm_start, (void *)vma->vm_end, - vma->vm_flags & VM_READ ? 'r' : '-', - vma->vm_flags & VM_WRITE ? 'w' : '-', - vma->vm_flags & VM_EXEC ? 'x' : '-', - vma->vm_flags & VM_MAYSHARE ? 's' : 'p', - vma->vm_flags & VM_LOCKED ? 'l' : '-', - vma->vm_flags & VM_IO ? 'i' : '-', - vma->vm_pgoff); - -#if defined(__i386__) - pgprot = pgprot_val(vma->vm_page_prot); - seq_printf(m, " %c%c%c%c%c%c%c%c%c", - pgprot & _PAGE_PRESENT ? 'p' : '-', - pgprot & _PAGE_RW ? 'w' : 'r', - pgprot & _PAGE_USER ? 'u' : 's', - pgprot & _PAGE_PWT ? 't' : 'b', - pgprot & _PAGE_PCD ? 'u' : 'c', - pgprot & _PAGE_ACCESSED ? 'a' : '-', - pgprot & _PAGE_DIRTY ? 'd' : '-', - pgprot & _PAGE_PSE ? 'm' : 'k', - pgprot & _PAGE_GLOBAL ? 'g' : 'l'); -#endif - seq_printf(m, "\n"); - } - mutex_unlock(&dev->struct_mutex); - return 0; -} - -#endif - diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h new file mode 100644 index 0000000000000000000000000000000000000000..7cc0a3516871f493e74a74a2d38b600e0802c3c4 --- /dev/null +++ b/drivers/gpu/drm/drm_internal.h @@ -0,0 +1,132 @@ +/* + * Copyright © 2014 Intel Corporation + * Daniel Vetter + * + * 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. + */ + +/* drm_irq.c */ +extern unsigned int drm_timestamp_monotonic; + +/* drm_fops.c */ +extern struct mutex drm_global_mutex; +int drm_lastclose(struct drm_device *dev); + +/* drm_pci.c */ +int drm_pci_set_unique(struct drm_device *dev, + struct drm_master *master, + struct drm_unique *u); +int drm_irq_by_busid(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +/* drm_vm.c */ +int drm_vma_info(struct seq_file *m, void *data); +void drm_vm_open_locked(struct drm_device *dev, struct vm_area_struct *vma); +void drm_vm_close_locked(struct drm_device *dev, struct vm_area_struct *vma); + +/* drm_prime.c */ +int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_prime_fd_to_handle_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv); +void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv); +void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpriv, + struct dma_buf *dma_buf); + +/* drm_info.c */ +int drm_name_info(struct seq_file *m, void *data); +int drm_vm_info(struct seq_file *m, void *data); +int drm_bufs_info(struct seq_file *m, void *data); +int drm_vblank_info(struct seq_file *m, void *data); +int drm_clients_info(struct seq_file *m, void* data); +int drm_gem_name_info(struct seq_file *m, void *data); + +/* drm_irq.c */ +int drm_control(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_modeset_ctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +/* drm_auth.c */ +int drm_getmagic(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_authmagic(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_remove_magic(struct drm_master *master, drm_magic_t magic); + +/* drm_sysfs.c */ +extern struct class *drm_class; + +struct class *drm_sysfs_create(struct module *owner, char *name); +void drm_sysfs_destroy(void); +struct device *drm_sysfs_minor_alloc(struct drm_minor *minor); +int drm_sysfs_connector_add(struct drm_connector *connector); +void drm_sysfs_connector_remove(struct drm_connector *connector); + +/* drm_gem.c */ +int drm_gem_init(struct drm_device *dev); +void drm_gem_destroy(struct drm_device *dev); +int drm_gem_handle_create_tail(struct drm_file *file_priv, + struct drm_gem_object *obj, + u32 *handlep); +int drm_gem_close_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_gem_flink_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_gem_open_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +void drm_gem_open(struct drm_device *dev, struct drm_file *file_private); +void drm_gem_release(struct drm_device *dev, struct drm_file *file_private); + +/* drm_drv.c */ +int drm_setmaster_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_dropmaster_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +struct drm_master *drm_master_create(struct drm_minor *minor); + +/* drm_debugfs.c */ +#if defined(CONFIG_DEBUG_FS) +int drm_debugfs_init(struct drm_minor *minor, int minor_id, + struct dentry *root); +int drm_debugfs_cleanup(struct drm_minor *minor); +int drm_debugfs_connector_add(struct drm_connector *connector); +void drm_debugfs_connector_remove(struct drm_connector *connector); +#else +static inline int drm_debugfs_init(struct drm_minor *minor, int minor_id, + struct dentry *root) +{ + return 0; +} + +static inline int drm_debugfs_cleanup(struct drm_minor *minor) +{ + return 0; +} + +static inline int drm_debugfs_connector_add(struct drm_connector *connector) +{ + return 0; +} +static inline void drm_debugfs_connector_remove(struct drm_connector *connector) +{ +} +#endif diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 40be746b7e685ef2d205e700f15ca5c3c38a8afe..00587a1e3c83c0dc23c05a55a7d37ea10f891847 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -31,6 +31,7 @@ #include #include #include "drm_legacy.h" +#include "drm_internal.h" #include #include @@ -41,121 +42,6 @@ static int drm_version(struct drm_device *dev, void *data, struct drm_file *file_priv); -#define DRM_IOCTL_DEF(ioctl, _func, _flags) \ - [DRM_IOCTL_NR(ioctl)] = {.cmd = ioctl, .func = _func, .flags = _flags, .cmd_drv = 0, .name = #ioctl} - -/** Ioctl table */ -static const struct drm_ioctl_desc drm_ioctls[] = { - DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_UNLOCKED|DRM_RENDER_ALLOW), - DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0), - DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0), - DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_getmap, DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, DRM_UNLOCKED|DRM_RENDER_ALLOW), - DRM_IOCTL_DEF(DRM_IOCTL_SET_CLIENT_CAP, drm_setclientcap, 0), - DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER), - - DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER), - - DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_addmap_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_RM_MAP, drm_rmmap_ioctl, DRM_AUTH), - - DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_legacy_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_legacy_getsareactx, DRM_AUTH), - - DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY), - - DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_legacy_addctx, DRM_AUTH|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_legacy_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_GET_CTX, drm_legacy_getctx, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_SWITCH_CTX, drm_legacy_switchctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_NEW_CTX, drm_legacy_newctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_RES_CTX, drm_legacy_resctx, DRM_AUTH), - - DRM_IOCTL_DEF(DRM_IOCTL_ADD_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_RM_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - - DRM_IOCTL_DEF(DRM_IOCTL_LOCK, drm_lock, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_UNLOCK, drm_unlock, DRM_AUTH), - - DRM_IOCTL_DEF(DRM_IOCTL_FINISH, drm_noop, DRM_AUTH), - - DRM_IOCTL_DEF(DRM_IOCTL_ADD_BUFS, drm_addbufs, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_MARK_BUFS, drm_markbufs, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_INFO_BUFS, drm_infobufs, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_MAP_BUFS, drm_mapbufs, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_FREE_BUFS, drm_freebufs, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_DMA, drm_dma_ioctl, DRM_AUTH), - - DRM_IOCTL_DEF(DRM_IOCTL_CONTROL, drm_control, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - -#if __OS_HAS_AGP - DRM_IOCTL_DEF(DRM_IOCTL_AGP_ACQUIRE, drm_agp_acquire_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_AGP_RELEASE, drm_agp_release_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_AGP_ENABLE, drm_agp_enable_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_AGP_INFO, drm_agp_info_ioctl, DRM_AUTH), - DRM_IOCTL_DEF(DRM_IOCTL_AGP_ALLOC, drm_agp_alloc_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_AGP_FREE, drm_agp_free_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_AGP_BIND, drm_agp_bind_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_AGP_UNBIND, drm_agp_unbind_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), -#endif - - DRM_IOCTL_DEF(DRM_IOCTL_SG_ALLOC, drm_sg_alloc, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_SG_FREE, drm_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - - DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, DRM_UNLOCKED), - - DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_modeset_ctl, 0), - - DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - - DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), - DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH|DRM_UNLOCKED), - - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - - DRM_IOCTL_DEF(DRM_IOCTL_PRIME_HANDLE_TO_FD, drm_prime_handle_to_fd_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), - DRM_IOCTL_DEF(DRM_IOCTL_PRIME_FD_TO_HANDLE, drm_prime_fd_to_handle_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), - - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPLANE, drm_mode_setplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_noop, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_noop, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), -}; - -#define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) - /** * Get the bus id. * @@ -167,7 +53,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { * * Copies the bus id from drm_device::unique into user space. */ -int drm_getunique(struct drm_device *dev, void *data, +static int drm_getunique(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_unique *u = data; @@ -189,7 +75,6 @@ drm_unset_busid(struct drm_device *dev, kfree(master->unique); master->unique = NULL; master->unique_len = 0; - master->unique_size = 0; } /** @@ -207,7 +92,7 @@ drm_unset_busid(struct drm_device *dev, * version 1.1 or greater. Also note that KMS is all version 1.1 and later and * UMS was only ever supported on pci devices. */ -int drm_setunique(struct drm_device *dev, void *data, +static int drm_setunique(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_unique *u = data; @@ -245,15 +130,15 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv) if (master->unique != NULL) drm_unset_busid(dev, master); - if (dev->driver->bus && dev->driver->bus->set_busid) { - ret = dev->driver->bus->set_busid(dev, master); + if (dev->driver->set_busid) { + ret = dev->driver->set_busid(dev, master); if (ret) { drm_unset_busid(dev, master); return ret; } } else { if (WARN(dev->unique == NULL, - "No drm_bus.set_busid() implementation provided by " + "No drm_driver.set_busid() implementation provided by " "%ps. Use drm_dev_set_unique() to set the unique " "name explicitly.", dev->driver)) return -EINVAL; @@ -279,7 +164,7 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv) * Searches for the mapping with the specified offset and copies its information * into userspace */ -int drm_getmap(struct drm_device *dev, void *data, +static int drm_getmap(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_map *map = data; @@ -340,7 +225,7 @@ int drm_getmap(struct drm_device *dev, void *data, * Searches for the client with the specified index and copies its information * into userspace */ -int drm_getclient(struct drm_device *dev, void *data, +static int drm_getclient(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_client *client = data; @@ -380,7 +265,7 @@ int drm_getclient(struct drm_device *dev, void *data, * * \return zero on success or a negative number on failure. */ -int drm_getstats(struct drm_device *dev, void *data, +static int drm_getstats(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_stats *stats = data; @@ -394,7 +279,7 @@ int drm_getstats(struct drm_device *dev, void *data, /** * Get device/driver capabilities */ -int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv) +static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_get_cap *req = data; @@ -444,7 +329,7 @@ int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv) /** * Set device/driver capabilities */ -int +static int drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_set_client_cap *req = data; @@ -478,7 +363,7 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv) * * Sets the requested interface version */ -int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_priv) +static int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_set_version *sv = data; int if_version, retcode = 0; @@ -624,6 +509,121 @@ static int drm_ioctl_permit(u32 flags, struct drm_file *file_priv) return 0; } +#define DRM_IOCTL_DEF(ioctl, _func, _flags) \ + [DRM_IOCTL_NR(ioctl)] = {.cmd = ioctl, .func = _func, .flags = _flags, .cmd_drv = 0, .name = #ioctl} + +/** Ioctl table */ +static const struct drm_ioctl_desc drm_ioctls[] = { + DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_UNLOCKED|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0), + DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0), + DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_getmap, DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, DRM_UNLOCKED|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_SET_CLIENT_CAP, drm_setclientcap, 0), + DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER), + + DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER), + + DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_legacy_addmap_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_RM_MAP, drm_legacy_rmmap_ioctl, DRM_AUTH), + + DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_legacy_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_legacy_getsareactx, DRM_AUTH), + + DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY), + + DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_legacy_addctx, DRM_AUTH|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_legacy_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_GET_CTX, drm_legacy_getctx, DRM_AUTH), + DRM_IOCTL_DEF(DRM_IOCTL_SWITCH_CTX, drm_legacy_switchctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_NEW_CTX, drm_legacy_newctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_RES_CTX, drm_legacy_resctx, DRM_AUTH), + + DRM_IOCTL_DEF(DRM_IOCTL_ADD_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_RM_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + + DRM_IOCTL_DEF(DRM_IOCTL_LOCK, drm_legacy_lock, DRM_AUTH), + DRM_IOCTL_DEF(DRM_IOCTL_UNLOCK, drm_legacy_unlock, DRM_AUTH), + + DRM_IOCTL_DEF(DRM_IOCTL_FINISH, drm_noop, DRM_AUTH), + + DRM_IOCTL_DEF(DRM_IOCTL_ADD_BUFS, drm_legacy_addbufs, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_MARK_BUFS, drm_legacy_markbufs, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_INFO_BUFS, drm_legacy_infobufs, DRM_AUTH), + DRM_IOCTL_DEF(DRM_IOCTL_MAP_BUFS, drm_legacy_mapbufs, DRM_AUTH), + DRM_IOCTL_DEF(DRM_IOCTL_FREE_BUFS, drm_legacy_freebufs, DRM_AUTH), + DRM_IOCTL_DEF(DRM_IOCTL_DMA, drm_legacy_dma_ioctl, DRM_AUTH), + + DRM_IOCTL_DEF(DRM_IOCTL_CONTROL, drm_control, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + +#if __OS_HAS_AGP + DRM_IOCTL_DEF(DRM_IOCTL_AGP_ACQUIRE, drm_agp_acquire_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_AGP_RELEASE, drm_agp_release_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_AGP_ENABLE, drm_agp_enable_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_AGP_INFO, drm_agp_info_ioctl, DRM_AUTH), + DRM_IOCTL_DEF(DRM_IOCTL_AGP_ALLOC, drm_agp_alloc_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_AGP_FREE, drm_agp_free_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_AGP_BIND, drm_agp_bind_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_AGP_UNBIND, drm_agp_unbind_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), +#endif + + DRM_IOCTL_DEF(DRM_IOCTL_SG_ALLOC, drm_legacy_sg_alloc, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_SG_FREE, drm_legacy_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + + DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, DRM_UNLOCKED), + + DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_modeset_ctl, 0), + + DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + + DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH|DRM_UNLOCKED), + + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + + DRM_IOCTL_DEF(DRM_IOCTL_PRIME_HANDLE_TO_FD, drm_prime_handle_to_fd_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_PRIME_FD_TO_HANDLE, drm_prime_fd_to_handle_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), + + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPLANE, drm_mode_setplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_noop, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_noop, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), +}; + +#define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) + /** * Called whenever a process performs an ioctl on /dev/drm. * diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 08ba1209228edf84d88883532b65c4708575e15a..5ef03c216a2740818468ef64ebb1a056f1644235 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -34,6 +34,7 @@ #include #include "drm_trace.h" +#include "drm_internal.h" #include /* For task queue support */ #include @@ -55,12 +56,91 @@ */ #define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000 +static bool +drm_get_last_vbltimestamp(struct drm_device *dev, int crtc, + struct timeval *tvblank, unsigned flags); + +static unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ + /* - * Clear vblank timestamp buffer for a crtc. + * Default to use monotonic timestamps for wait-for-vblank and page-flip + * complete events. + */ +unsigned int drm_timestamp_monotonic = 1; + +static int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */ + +module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); +module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); +module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); + +/** + * drm_update_vblank_count - update the master vblank counter + * @dev: DRM device + * @crtc: counter to update + * + * Call back into the driver to update the appropriate vblank counter + * (specified by @crtc). Deal with wraparound, if it occurred, and + * update the last read value so we can deal with wraparound on the next + * call if necessary. + * + * Only necessary when going from off->on, to account for frames we + * didn't get an interrupt for. + * + * Note: caller must hold dev->vbl_lock since this reads & writes + * device vblank fields. */ -static void clear_vblank_timestamps(struct drm_device *dev, int crtc) +static void drm_update_vblank_count(struct drm_device *dev, int crtc) { - memset(dev->vblank[crtc].time, 0, sizeof(dev->vblank[crtc].time)); + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; + u32 cur_vblank, diff, tslot; + bool rc; + struct timeval t_vblank; + + /* + * Interrupts were disabled prior to this call, so deal with counter + * wrap if needed. + * NOTE! It's possible we lost a full dev->max_vblank_count events + * here if the register is small or we had vblank interrupts off for + * a long time. + * + * We repeat the hardware vblank counter & timestamp query until + * we get consistent results. This to prevent races between gpu + * updating its hardware counter while we are retrieving the + * corresponding vblank timestamp. + */ + do { + cur_vblank = dev->driver->get_vblank_counter(dev, crtc); + rc = drm_get_last_vbltimestamp(dev, crtc, &t_vblank, 0); + } while (cur_vblank != dev->driver->get_vblank_counter(dev, crtc)); + + /* Deal with counter wrap */ + diff = cur_vblank - vblank->last; + if (cur_vblank < vblank->last) { + diff += dev->max_vblank_count; + + DRM_DEBUG("last_vblank[%d]=0x%x, cur_vblank=0x%x => diff=0x%x\n", + crtc, vblank->last, cur_vblank, diff); + } + + DRM_DEBUG("updating vblank count on crtc %d, missed %d\n", + crtc, diff); + + if (diff == 0) + return; + + /* Reinitialize corresponding vblank timestamp if high-precision query + * available. Skip this step if query unsupported or failed. Will + * reinitialize delayed at next vblank interrupt in that case. + */ + if (rc) { + tslot = atomic_read(&vblank->count) + diff; + vblanktimestamp(dev, crtc, tslot) = t_vblank; + } + + smp_mb__before_atomic(); + atomic_add(diff, &vblank->count); + smp_mb__after_atomic(); } /* @@ -71,10 +151,11 @@ static void clear_vblank_timestamps(struct drm_device *dev, int crtc) */ static void vblank_disable_and_save(struct drm_device *dev, int crtc) { + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; unsigned long irqflags; u32 vblcount; s64 diff_ns; - int vblrc; + bool vblrc; struct timeval tvblank; int count = DRM_TIMESTAMP_MAXRETRIES; @@ -84,8 +165,28 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc) */ spin_lock_irqsave(&dev->vblank_time_lock, irqflags); + /* + * If the vblank interrupt was already disbled update the count + * and timestamp to maintain the appearance that the counter + * has been ticking all along until this time. This makes the + * count account for the entire time between drm_vblank_on() and + * drm_vblank_off(). + * + * But only do this if precise vblank timestamps are available. + * Otherwise we might read a totally bogus timestamp since drivers + * lacking precise timestamp support rely upon sampling the system clock + * at vblank interrupt time. Which obviously won't work out well if the + * vblank interrupt is disabled. + */ + if (!vblank->enabled && + drm_get_last_vbltimestamp(dev, crtc, &tvblank, 0)) { + drm_update_vblank_count(dev, crtc); + spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); + return; + } + dev->driver->disable_vblank(dev, crtc); - dev->vblank[crtc].enabled = false; + vblank->enabled = false; /* No further vblank irq's will be processed after * this point. Get current hardware vblank count and @@ -100,9 +201,9 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc) * delayed gpu counter increment. */ do { - dev->vblank[crtc].last = dev->driver->get_vblank_counter(dev, crtc); + vblank->last = dev->driver->get_vblank_counter(dev, crtc); vblrc = drm_get_last_vbltimestamp(dev, crtc, &tvblank, 0); - } while (dev->vblank[crtc].last != dev->driver->get_vblank_counter(dev, crtc) && (--count) && vblrc); + } while (vblank->last != dev->driver->get_vblank_counter(dev, crtc) && (--count) && vblrc); if (!count) vblrc = 0; @@ -110,7 +211,7 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc) /* Compute time difference to stored timestamp of last vblank * as updated by last invocation of drm_handle_vblank() in vblank irq. */ - vblcount = atomic_read(&dev->vblank[crtc].count); + vblcount = atomic_read(&vblank->count); diff_ns = timeval_to_ns(&tvblank) - timeval_to_ns(&vblanktimestamp(dev, crtc, vblcount)); @@ -126,14 +227,18 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc) * available. In that case we can't account for this and just * hope for the best. */ - if ((vblrc > 0) && (abs64(diff_ns) > 1000000)) { - atomic_inc(&dev->vblank[crtc].count); + if (vblrc && (abs64(diff_ns) > 1000000)) { + /* Store new timestamp in ringbuffer. */ + vblanktimestamp(dev, crtc, vblcount + 1) = tvblank; + + /* Increment cooked vblank count. This also atomically commits + * the timestamp computed above. + */ + smp_mb__before_atomic(); + atomic_inc(&vblank->count); smp_mb__after_atomic(); } - /* Invalidate all timestamps while vblank irq's are off. */ - clear_vblank_timestamps(dev, crtc); - spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); } @@ -164,14 +269,20 @@ static void vblank_disable_fn(unsigned long arg) void drm_vblank_cleanup(struct drm_device *dev) { int crtc; + unsigned long irqflags; /* Bail if the driver didn't call drm_vblank_init() */ if (dev->num_crtcs == 0) return; for (crtc = 0; crtc < dev->num_crtcs; crtc++) { - del_timer_sync(&dev->vblank[crtc].disable_timer); - vblank_disable_fn((unsigned long)&dev->vblank[crtc]); + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; + + del_timer_sync(&vblank->disable_timer); + + spin_lock_irqsave(&dev->vbl_lock, irqflags); + vblank_disable_and_save(dev, crtc); + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); } kfree(dev->vblank); @@ -204,11 +315,13 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) goto err; for (i = 0; i < num_crtcs; i++) { - dev->vblank[i].dev = dev; - dev->vblank[i].crtc = i; - init_waitqueue_head(&dev->vblank[i].queue); - setup_timer(&dev->vblank[i].disable_timer, vblank_disable_fn, - (unsigned long)&dev->vblank[i]); + struct drm_vblank_crtc *vblank = &dev->vblank[i]; + + vblank->dev = dev; + vblank->crtc = i; + init_waitqueue_head(&vblank->queue); + setup_timer(&vblank->disable_timer, vblank_disable_fn, + (unsigned long)vblank); } DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n"); @@ -224,7 +337,7 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) return 0; err: - drm_vblank_cleanup(dev); + dev->num_crtcs = 0; return ret; } EXPORT_SYMBOL(drm_vblank_init); @@ -360,9 +473,11 @@ int drm_irq_uninstall(struct drm_device *dev) if (dev->num_crtcs) { spin_lock_irqsave(&dev->vbl_lock, irqflags); for (i = 0; i < dev->num_crtcs; i++) { - wake_up(&dev->vblank[i].queue); - dev->vblank[i].enabled = false; - dev->vblank[i].last = + struct drm_vblank_crtc *vblank = &dev->vblank[i]; + + wake_up(&vblank->queue); + vblank->enabled = false; + vblank->last = dev->driver->get_vblank_counter(dev, i); } spin_unlock_irqrestore(&dev->vbl_lock, irqflags); @@ -617,7 +732,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, * within vblank area, counting down the number of lines until * start of scanout. */ - invbl = vbl_status & DRM_SCANOUTPOS_INVBL; + invbl = vbl_status & DRM_SCANOUTPOS_IN_VBLANK; /* Convert scanout position into elapsed time at raw_time query * since start of scanout at first display scanline. delta_ns @@ -647,7 +762,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, vbl_status = DRM_VBLANKTIME_SCANOUTPOS_METHOD; if (invbl) - vbl_status |= DRM_VBLANKTIME_INVBL; + vbl_status |= DRM_VBLANKTIME_IN_VBLANK; return vbl_status; } @@ -679,10 +794,11 @@ static struct timeval get_drm_timestamp(void) * call, i.e., it isn't very precisely locked to the true vblank. * * Returns: - * Non-zero if timestamp is considered to be very precise, zero otherwise. + * True if timestamp is considered to be very precise, false otherwise. */ -u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc, - struct timeval *tvblank, unsigned flags) +static bool +drm_get_last_vbltimestamp(struct drm_device *dev, int crtc, + struct timeval *tvblank, unsigned flags) { int ret; @@ -694,7 +810,7 @@ u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc, ret = dev->driver->get_vblank_timestamp(dev, crtc, &max_error, tvblank, flags); if (ret > 0) - return (u32) ret; + return true; } /* GPU high precision timestamp query unsupported or failed. @@ -702,9 +818,8 @@ u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc, */ *tvblank = get_drm_timestamp(); - return 0; + return false; } -EXPORT_SYMBOL(drm_get_last_vbltimestamp); /** * drm_vblank_count - retrieve "cooked" vblank counter value @@ -720,7 +835,11 @@ EXPORT_SYMBOL(drm_get_last_vbltimestamp); */ u32 drm_vblank_count(struct drm_device *dev, int crtc) { - return atomic_read(&dev->vblank[crtc].count); + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; + + if (WARN_ON(crtc >= dev->num_crtcs)) + return 0; + return atomic_read(&vblank->count); } EXPORT_SYMBOL(drm_vblank_count); @@ -740,18 +859,22 @@ EXPORT_SYMBOL(drm_vblank_count); u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc, struct timeval *vblanktime) { + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; u32 cur_vblank; + if (WARN_ON(crtc >= dev->num_crtcs)) + return 0; + /* Read timestamp from slot of _vblank_time ringbuffer * that corresponds to current vblank count. Retry if * count has incremented during readout. This works like * a seqlock. */ do { - cur_vblank = atomic_read(&dev->vblank[crtc].count); + cur_vblank = atomic_read(&vblank->count); *vblanktime = vblanktimestamp(dev, crtc, cur_vblank); smp_rmb(); - } while (cur_vblank != atomic_read(&dev->vblank[crtc].count)); + } while (cur_vblank != atomic_read(&vblank->count)); return cur_vblank; } @@ -799,70 +922,6 @@ void drm_send_vblank_event(struct drm_device *dev, int crtc, } EXPORT_SYMBOL(drm_send_vblank_event); -/** - * drm_update_vblank_count - update the master vblank counter - * @dev: DRM device - * @crtc: counter to update - * - * Call back into the driver to update the appropriate vblank counter - * (specified by @crtc). Deal with wraparound, if it occurred, and - * update the last read value so we can deal with wraparound on the next - * call if necessary. - * - * Only necessary when going from off->on, to account for frames we - * didn't get an interrupt for. - * - * Note: caller must hold dev->vbl_lock since this reads & writes - * device vblank fields. - */ -static void drm_update_vblank_count(struct drm_device *dev, int crtc) -{ - u32 cur_vblank, diff, tslot, rc; - struct timeval t_vblank; - - /* - * Interrupts were disabled prior to this call, so deal with counter - * wrap if needed. - * NOTE! It's possible we lost a full dev->max_vblank_count events - * here if the register is small or we had vblank interrupts off for - * a long time. - * - * We repeat the hardware vblank counter & timestamp query until - * we get consistent results. This to prevent races between gpu - * updating its hardware counter while we are retrieving the - * corresponding vblank timestamp. - */ - do { - cur_vblank = dev->driver->get_vblank_counter(dev, crtc); - rc = drm_get_last_vbltimestamp(dev, crtc, &t_vblank, 0); - } while (cur_vblank != dev->driver->get_vblank_counter(dev, crtc)); - - /* Deal with counter wrap */ - diff = cur_vblank - dev->vblank[crtc].last; - if (cur_vblank < dev->vblank[crtc].last) { - diff += dev->max_vblank_count; - - DRM_DEBUG("last_vblank[%d]=0x%x, cur_vblank=0x%x => diff=0x%x\n", - crtc, dev->vblank[crtc].last, cur_vblank, diff); - } - - DRM_DEBUG("enabling vblank interrupts on crtc %d, missed %d\n", - crtc, diff); - - /* Reinitialize corresponding vblank timestamp if high-precision query - * available. Skip this step if query unsupported or failed. Will - * reinitialize delayed at next vblank interrupt in that case. - */ - if (rc) { - tslot = atomic_read(&dev->vblank[crtc].count) + diff; - vblanktimestamp(dev, crtc, tslot) = t_vblank; - } - - smp_mb__before_atomic(); - atomic_add(diff, &dev->vblank[crtc].count); - smp_mb__after_atomic(); -} - /** * drm_vblank_enable - enable the vblank interrupt on a CRTC * @dev: DRM device @@ -870,13 +929,14 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) */ static int drm_vblank_enable(struct drm_device *dev, int crtc) { + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; int ret = 0; assert_spin_locked(&dev->vbl_lock); spin_lock(&dev->vblank_time_lock); - if (!dev->vblank[crtc].enabled) { + if (!vblank->enabled) { /* * Enable vblank irqs under vblank_time_lock protection. * All vblank count & timestamp updates are held off @@ -887,9 +947,9 @@ static int drm_vblank_enable(struct drm_device *dev, int crtc) ret = dev->driver->enable_vblank(dev, crtc); DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret); if (ret) - atomic_dec(&dev->vblank[crtc].refcount); + atomic_dec(&vblank->refcount); else { - dev->vblank[crtc].enabled = true; + vblank->enabled = true; drm_update_vblank_count(dev, crtc); } } @@ -914,16 +974,20 @@ static int drm_vblank_enable(struct drm_device *dev, int crtc) */ int drm_vblank_get(struct drm_device *dev, int crtc) { + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; unsigned long irqflags; int ret = 0; + if (WARN_ON(crtc >= dev->num_crtcs)) + return -EINVAL; + spin_lock_irqsave(&dev->vbl_lock, irqflags); /* Going from 0->1 means we have to enable interrupts again */ - if (atomic_add_return(1, &dev->vblank[crtc].refcount) == 1) { + if (atomic_add_return(1, &vblank->refcount) == 1) { ret = drm_vblank_enable(dev, crtc); } else { - if (!dev->vblank[crtc].enabled) { - atomic_dec(&dev->vblank[crtc].refcount); + if (!vblank->enabled) { + atomic_dec(&vblank->refcount); ret = -EINVAL; } } @@ -963,13 +1027,23 @@ EXPORT_SYMBOL(drm_crtc_vblank_get); */ void drm_vblank_put(struct drm_device *dev, int crtc) { - BUG_ON(atomic_read(&dev->vblank[crtc].refcount) == 0); + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; + + BUG_ON(atomic_read(&vblank->refcount) == 0); + + if (WARN_ON(crtc >= dev->num_crtcs)) + return; /* Last user schedules interrupt disable */ - if (atomic_dec_and_test(&dev->vblank[crtc].refcount) && - (drm_vblank_offdelay > 0)) - mod_timer(&dev->vblank[crtc].disable_timer, - jiffies + ((drm_vblank_offdelay * HZ)/1000)); + if (atomic_dec_and_test(&vblank->refcount)) { + if (drm_vblank_offdelay == 0) + return; + else if (dev->vblank_disable_immediate || drm_vblank_offdelay < 0) + vblank_disable_fn((unsigned long)vblank); + else + mod_timer(&vblank->disable_timer, + jiffies + ((drm_vblank_offdelay * HZ)/1000)); + } } EXPORT_SYMBOL(drm_vblank_put); @@ -988,6 +1062,50 @@ void drm_crtc_vblank_put(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_crtc_vblank_put); +/** + * drm_wait_one_vblank - wait for one vblank + * @dev: DRM device + * @crtc: crtc index + * + * This waits for one vblank to pass on @crtc, using the irq driver interfaces. + * It is a failure to call this when the vblank irq for @crtc is disabled, e.g. + * due to lack of driver support or because the crtc is off. + */ +void drm_wait_one_vblank(struct drm_device *dev, int crtc) +{ + int ret; + u32 last; + + ret = drm_vblank_get(dev, crtc); + if (WARN(ret, "vblank not available on crtc %i, ret=%i\n", crtc, ret)) + return; + + last = drm_vblank_count(dev, crtc); + + ret = wait_event_timeout(dev->vblank[crtc].queue, + last != drm_vblank_count(dev, crtc), + msecs_to_jiffies(100)); + + WARN(ret == 0, "vblank wait timed out on crtc %i\n", crtc); + + drm_vblank_put(dev, crtc); +} +EXPORT_SYMBOL(drm_wait_one_vblank); + +/** + * drm_crtc_wait_one_vblank - wait for one vblank + * @crtc: DRM crtc + * + * This waits for one vblank to pass on @crtc, using the irq driver interfaces. + * It is a failure to call this when the vblank irq for @crtc is disabled, e.g. + * due to lack of driver support or because the crtc is off. + */ +void drm_crtc_wait_one_vblank(struct drm_crtc *crtc) +{ + drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_wait_one_vblank); + /** * drm_vblank_off - disable vblank events on a CRTC * @dev: DRM device @@ -1004,19 +1122,34 @@ EXPORT_SYMBOL(drm_crtc_vblank_put); */ void drm_vblank_off(struct drm_device *dev, int crtc) { + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; struct drm_pending_vblank_event *e, *t; struct timeval now; unsigned long irqflags; unsigned int seq; - spin_lock_irqsave(&dev->vbl_lock, irqflags); + if (WARN_ON(crtc >= dev->num_crtcs)) + return; + + spin_lock_irqsave(&dev->event_lock, irqflags); + + spin_lock(&dev->vbl_lock); vblank_disable_and_save(dev, crtc); - wake_up(&dev->vblank[crtc].queue); + wake_up(&vblank->queue); + + /* + * Prevent subsequent drm_vblank_get() from re-enabling + * the vblank interrupt by bumping the refcount. + */ + if (!vblank->inmodeset) { + atomic_inc(&vblank->refcount); + vblank->inmodeset = 1; + } + spin_unlock(&dev->vbl_lock); /* Send any queued vblank events, lest the natives grow disquiet */ seq = drm_vblank_count_and_time(dev, crtc, &now); - spin_lock(&dev->event_lock); list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { if (e->pipe != crtc) continue; @@ -1027,9 +1160,7 @@ void drm_vblank_off(struct drm_device *dev, int crtc) drm_vblank_put(dev, e->pipe); send_vblank_event(dev, e, seq, &now); } - spin_unlock(&dev->event_lock); - - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + spin_unlock_irqrestore(&dev->event_lock, irqflags); } EXPORT_SYMBOL(drm_vblank_off); @@ -1066,11 +1197,35 @@ EXPORT_SYMBOL(drm_crtc_vblank_off); */ void drm_vblank_on(struct drm_device *dev, int crtc) { + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; unsigned long irqflags; + if (WARN_ON(crtc >= dev->num_crtcs)) + return; + spin_lock_irqsave(&dev->vbl_lock, irqflags); - /* re-enable interrupts if there's are users left */ - if (atomic_read(&dev->vblank[crtc].refcount) != 0) + /* Drop our private "prevent drm_vblank_get" refcount */ + if (vblank->inmodeset) { + atomic_dec(&vblank->refcount); + vblank->inmodeset = 0; + } + + /* + * sample the current counter to avoid random jumps + * when drm_vblank_enable() applies the diff + * + * -1 to make sure user will never see the same + * vblank counter value before and after a modeset + */ + vblank->last = + (dev->driver->get_vblank_counter(dev, crtc) - 1) & + dev->max_vblank_count; + /* + * re-enable interrupts if there are users left, or the + * user wishes vblank interrupts to be enabled all the time. + */ + if (atomic_read(&vblank->refcount) != 0 || + (!dev->vblank_disable_immediate && drm_vblank_offdelay == 0)) WARN_ON(drm_vblank_enable(dev, crtc)); spin_unlock_irqrestore(&dev->vbl_lock, irqflags); } @@ -1118,9 +1273,15 @@ EXPORT_SYMBOL(drm_crtc_vblank_on); */ void drm_vblank_pre_modeset(struct drm_device *dev, int crtc) { + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; + /* vblank is not initialized (IRQ not installed ?), or has been freed */ if (!dev->num_crtcs) return; + + if (WARN_ON(crtc >= dev->num_crtcs)) + return; + /* * To avoid all the problems that might happen if interrupts * were enabled/disabled around or between these calls, we just @@ -1128,10 +1289,10 @@ void drm_vblank_pre_modeset(struct drm_device *dev, int crtc) * to avoid corrupting the count if multiple, mismatch calls occur), * so that interrupts remain enabled in the interim. */ - if (!dev->vblank[crtc].inmodeset) { - dev->vblank[crtc].inmodeset = 0x1; + if (!vblank->inmodeset) { + vblank->inmodeset = 0x1; if (drm_vblank_get(dev, crtc) == 0) - dev->vblank[crtc].inmodeset |= 0x2; + vblank->inmodeset |= 0x2; } } EXPORT_SYMBOL(drm_vblank_pre_modeset); @@ -1146,21 +1307,22 @@ EXPORT_SYMBOL(drm_vblank_pre_modeset); */ void drm_vblank_post_modeset(struct drm_device *dev, int crtc) { + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; unsigned long irqflags; /* vblank is not initialized (IRQ not installed ?), or has been freed */ if (!dev->num_crtcs) return; - if (dev->vblank[crtc].inmodeset) { + if (vblank->inmodeset) { spin_lock_irqsave(&dev->vbl_lock, irqflags); dev->vblank_disable_allowed = true; spin_unlock_irqrestore(&dev->vbl_lock, irqflags); - if (dev->vblank[crtc].inmodeset & 0x2) + if (vblank->inmodeset & 0x2) drm_vblank_put(dev, crtc); - dev->vblank[crtc].inmodeset = 0; + vblank->inmodeset = 0; } } EXPORT_SYMBOL(drm_vblank_post_modeset); @@ -1212,6 +1374,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe, union drm_wait_vblank *vblwait, struct drm_file *file_priv) { + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; struct drm_pending_vblank_event *e; struct timeval now; unsigned long flags; @@ -1235,6 +1398,18 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe, spin_lock_irqsave(&dev->event_lock, flags); + /* + * drm_vblank_off() might have been called after we called + * drm_vblank_get(). drm_vblank_off() holds event_lock + * around the vblank disable, so no need for further locking. + * The reference from drm_vblank_get() protects against + * vblank disable from another source. + */ + if (!vblank->enabled) { + ret = -EINVAL; + goto err_unlock; + } + if (file_priv->event_space < sizeof e->event) { ret = -EBUSY; goto err_unlock; @@ -1295,6 +1470,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe, int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv) { + struct drm_vblank_crtc *vblank; union drm_wait_vblank *vblwait = data; int ret; unsigned int flags, seq, crtc, high_crtc; @@ -1324,6 +1500,8 @@ int drm_wait_vblank(struct drm_device *dev, void *data, if (crtc >= dev->num_crtcs) return -EINVAL; + vblank = &dev->vblank[crtc]; + ret = drm_vblank_get(dev, crtc); if (ret) { DRM_DEBUG("failed to acquire vblank counter, %d\n", ret); @@ -1356,11 +1534,11 @@ int drm_wait_vblank(struct drm_device *dev, void *data, DRM_DEBUG("waiting on vblank count %d, crtc %d\n", vblwait->request.sequence, crtc); - dev->vblank[crtc].last_wait = vblwait->request.sequence; - DRM_WAIT_ON(ret, dev->vblank[crtc].queue, 3 * HZ, + vblank->last_wait = vblwait->request.sequence; + DRM_WAIT_ON(ret, vblank->queue, 3 * HZ, (((drm_vblank_count(dev, crtc) - vblwait->request.sequence) <= (1 << 23)) || - !dev->vblank[crtc].enabled || + !vblank->enabled || !dev->irq_enabled)); if (ret != -EINTR) { @@ -1385,12 +1563,11 @@ static void drm_handle_vblank_events(struct drm_device *dev, int crtc) { struct drm_pending_vblank_event *e, *t; struct timeval now; - unsigned long flags; unsigned int seq; - seq = drm_vblank_count_and_time(dev, crtc, &now); + assert_spin_locked(&dev->event_lock); - spin_lock_irqsave(&dev->event_lock, flags); + seq = drm_vblank_count_and_time(dev, crtc, &now); list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { if (e->pipe != crtc) @@ -1406,8 +1583,6 @@ static void drm_handle_vblank_events(struct drm_device *dev, int crtc) send_vblank_event(dev, e, seq, &now); } - spin_unlock_irqrestore(&dev->event_lock, flags); - trace_drm_vblank_event(crtc, seq); } @@ -1421,6 +1596,7 @@ static void drm_handle_vblank_events(struct drm_device *dev, int crtc) */ bool drm_handle_vblank(struct drm_device *dev, int crtc) { + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; u32 vblcount; s64 diff_ns; struct timeval tvblank; @@ -1429,15 +1605,21 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc) if (!dev->num_crtcs) return false; + if (WARN_ON(crtc >= dev->num_crtcs)) + return false; + + spin_lock_irqsave(&dev->event_lock, irqflags); + /* Need timestamp lock to prevent concurrent execution with * vblank enable/disable, as this would cause inconsistent * or corrupted timestamps and vblank counts. */ - spin_lock_irqsave(&dev->vblank_time_lock, irqflags); + spin_lock(&dev->vblank_time_lock); /* Vblank irq handling disabled. Nothing to do. */ - if (!dev->vblank[crtc].enabled) { - spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); + if (!vblank->enabled) { + spin_unlock(&dev->vblank_time_lock); + spin_unlock_irqrestore(&dev->event_lock, irqflags); return false; } @@ -1446,7 +1628,7 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc) */ /* Get current timestamp and count. */ - vblcount = atomic_read(&dev->vblank[crtc].count); + vblcount = atomic_read(&vblank->count); drm_get_last_vbltimestamp(dev, crtc, &tvblank, DRM_CALLED_FROM_VBLIRQ); /* Compute time difference to timestamp of last vblank */ @@ -1470,17 +1652,20 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc) * the timestamp computed above. */ smp_mb__before_atomic(); - atomic_inc(&dev->vblank[crtc].count); + atomic_inc(&vblank->count); smp_mb__after_atomic(); } else { DRM_DEBUG("crtc %d: Redundant vblirq ignored. diff_ns = %d\n", crtc, (int) diff_ns); } - wake_up(&dev->vblank[crtc].queue); + spin_unlock(&dev->vblank_time_lock); + + wake_up(&vblank->queue); drm_handle_vblank_events(dev, crtc); - spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); + spin_unlock_irqrestore(&dev->event_lock, irqflags); + return true; } EXPORT_SYMBOL(drm_handle_vblank); diff --git a/drivers/gpu/drm/drm_legacy.h b/drivers/gpu/drm/drm_legacy.h index d34f20a79b7cdbe1e080682d18a00889d551e284..c1dc61473db58b1a518c65eae09f25f00fa6fffc 100644 --- a/drivers/gpu/drm/drm_legacy.h +++ b/drivers/gpu/drm/drm_legacy.h @@ -23,6 +23,15 @@ * OTHER DEALINGS IN THE SOFTWARE. */ +/* + * This file contains legacy interfaces that modern drm drivers + * should no longer be using. They cannot be removed as legacy + * drivers use them, and removing them are API breaks. + */ +#include +#include + +struct agp_memory; struct drm_device; struct drm_file; @@ -48,4 +57,57 @@ int drm_legacy_rmctx(struct drm_device *d, void *v, struct drm_file *f); int drm_legacy_setsareactx(struct drm_device *d, void *v, struct drm_file *f); int drm_legacy_getsareactx(struct drm_device *d, void *v, struct drm_file *f); +/* + * Generic Buffer Management + */ + +#define DRM_MAP_HASH_OFFSET 0x10000000 + +int drm_legacy_addmap_ioctl(struct drm_device *d, void *v, struct drm_file *f); +int drm_legacy_rmmap_ioctl(struct drm_device *d, void *v, struct drm_file *f); +int drm_legacy_addbufs(struct drm_device *d, void *v, struct drm_file *f); +int drm_legacy_infobufs(struct drm_device *d, void *v, struct drm_file *f); +int drm_legacy_markbufs(struct drm_device *d, void *v, struct drm_file *f); +int drm_legacy_freebufs(struct drm_device *d, void *v, struct drm_file *f); +int drm_legacy_mapbufs(struct drm_device *d, void *v, struct drm_file *f); +int drm_legacy_dma_ioctl(struct drm_device *d, void *v, struct drm_file *f); + +void drm_legacy_vma_flush(struct drm_device *d); + +/* + * AGP Support + */ + +struct drm_agp_mem { + unsigned long handle; + struct agp_memory *memory; + unsigned long bound; + int pages; + struct list_head head; +}; + +/* + * Generic Userspace Locking-API + */ + +int drm_legacy_i_have_hw_lock(struct drm_device *d, struct drm_file *f); +int drm_legacy_lock(struct drm_device *d, void *v, struct drm_file *f); +int drm_legacy_unlock(struct drm_device *d, void *v, struct drm_file *f); +int drm_legacy_lock_free(struct drm_lock_data *lock, unsigned int ctx); + +/* DMA support */ +int drm_legacy_dma_setup(struct drm_device *dev); +void drm_legacy_dma_takedown(struct drm_device *dev); +void drm_legacy_free_buffer(struct drm_device *dev, + struct drm_buf * buf); +void drm_legacy_reclaim_buffers(struct drm_device *dev, + struct drm_file *filp); + +/* Scatter Gather Support */ +void drm_legacy_sg_cleanup(struct drm_device *dev); +int drm_legacy_sg_alloc(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_legacy_sg_free(struct drm_device *dev, void *data, + struct drm_file *file_priv); + #endif /* __DRM_LEGACY_H__ */ diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c index e26b59e385ff151116f5af63bf47626c2839f8eb..f861361a635e05c3ab91e82fdde52b0f7f0e07e2 100644 --- a/drivers/gpu/drm/drm_lock.c +++ b/drivers/gpu/drm/drm_lock.c @@ -36,6 +36,7 @@ #include #include #include "drm_legacy.h" +#include "drm_internal.h" static int drm_notifier(void *priv); @@ -52,7 +53,8 @@ static int drm_lock_take(struct drm_lock_data *lock_data, unsigned int context); * * Add the current task to the lock wait queue, and attempt to take to lock. */ -int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv) +int drm_legacy_lock(struct drm_device *dev, void *data, + struct drm_file *file_priv) { DECLARE_WAITQUEUE(entry, current); struct drm_lock *lock = data; @@ -120,7 +122,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv) sigaddset(&dev->sigmask, SIGTTOU); dev->sigdata.context = lock->context; dev->sigdata.lock = master->lock.hw_lock; - block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask); + block_all_signals(drm_notifier, dev, &dev->sigmask); } if (dev->driver->dma_quiescent && (lock->flags & _DRM_LOCK_QUIESCENT)) @@ -146,7 +148,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv) * * Transfer and free the lock. */ -int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv) +int drm_legacy_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_lock *lock = data; struct drm_master *master = file_priv->master; @@ -157,7 +159,7 @@ int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv) return -EINVAL; } - if (drm_lock_free(&master->lock, lock->context)) { + if (drm_legacy_lock_free(&master->lock, lock->context)) { /* FIXME: Should really bail out here. */ } @@ -250,7 +252,7 @@ static int drm_lock_transfer(struct drm_lock_data *lock_data, * Marks the lock as not held, via the \p cmpxchg instruction. Wakes any task * waiting on the lock queue. */ -int drm_lock_free(struct drm_lock_data *lock_data, unsigned int context) +int drm_legacy_lock_free(struct drm_lock_data *lock_data, unsigned int context) { unsigned int old, new, prev; volatile unsigned int *lock = &lock_data->hw_lock->lock; @@ -286,26 +288,27 @@ int drm_lock_free(struct drm_lock_data *lock_data, unsigned int context) * If the lock is not held, then let the signal proceed as usual. If the lock * is held, then set the contended flag and keep the signal blocked. * - * \param priv pointer to a drm_sigdata structure. + * \param priv pointer to a drm_device structure. * \return one if the signal should be delivered normally, or zero if the * signal should be blocked. */ static int drm_notifier(void *priv) { - struct drm_sigdata *s = (struct drm_sigdata *) priv; + struct drm_device *dev = priv; + struct drm_hw_lock *lock = dev->sigdata.lock; unsigned int old, new, prev; /* Allow signal delivery if lock isn't held */ - if (!s->lock || !_DRM_LOCK_IS_HELD(s->lock->lock) - || _DRM_LOCKING_CONTEXT(s->lock->lock) != s->context) + if (!lock || !_DRM_LOCK_IS_HELD(lock->lock) + || _DRM_LOCKING_CONTEXT(lock->lock) != dev->sigdata.context) return 1; /* Otherwise, set flag to force call to drmUnlock */ do { - old = s->lock->lock; + old = lock->lock; new = old | _DRM_LOCK_CONT; - prev = cmpxchg(&s->lock->lock, old, new); + prev = cmpxchg(&lock->lock, old, new); } while (prev != old); return 0; } @@ -323,7 +326,7 @@ static int drm_notifier(void *priv) * having to worry about starvation. */ -void drm_idlelock_take(struct drm_lock_data *lock_data) +void drm_legacy_idlelock_take(struct drm_lock_data *lock_data) { int ret; @@ -340,9 +343,9 @@ void drm_idlelock_take(struct drm_lock_data *lock_data) } spin_unlock_bh(&lock_data->spinlock); } -EXPORT_SYMBOL(drm_idlelock_take); +EXPORT_SYMBOL(drm_legacy_idlelock_take); -void drm_idlelock_release(struct drm_lock_data *lock_data) +void drm_legacy_idlelock_release(struct drm_lock_data *lock_data) { unsigned int old, prev; volatile unsigned int *lock = &lock_data->hw_lock->lock; @@ -360,9 +363,10 @@ void drm_idlelock_release(struct drm_lock_data *lock_data) } spin_unlock_bh(&lock_data->spinlock); } -EXPORT_SYMBOL(drm_idlelock_release); +EXPORT_SYMBOL(drm_legacy_idlelock_release); -int drm_i_have_hw_lock(struct drm_device *dev, struct drm_file *file_priv) +int drm_legacy_i_have_hw_lock(struct drm_device *dev, + struct drm_file *file_priv) { struct drm_master *master = file_priv->master; return (file_priv->lock_count && master->lock.hw_lock && diff --git a/drivers/gpu/drm/drm_memory.c b/drivers/gpu/drm/drm_memory.c index 00c67c0f238127d1d36c8ba17e6caf184412c446..a521ef6ff8072ab7e65508b5da98b84dc058bfa0 100644 --- a/drivers/gpu/drm/drm_memory.c +++ b/drivers/gpu/drm/drm_memory.c @@ -36,8 +36,20 @@ #include #include #include +#include "drm_legacy.h" #if __OS_HAS_AGP + +#ifdef HAVE_PAGE_AGP +# include +#else +# ifdef __powerpc__ +# define PAGE_AGP __pgprot(_PAGE_KERNEL | _PAGE_NO_CACHE) +# else +# define PAGE_AGP PAGE_KERNEL +# endif +#endif + static void *agp_remap(unsigned long offset, unsigned long size, struct drm_device * dev) { @@ -108,25 +120,25 @@ static inline void *agp_remap(unsigned long offset, unsigned long size, #endif /* agp */ -void drm_core_ioremap(struct drm_local_map *map, struct drm_device *dev) +void drm_legacy_ioremap(struct drm_local_map *map, struct drm_device *dev) { if (dev->agp && dev->agp->cant_use_aperture && map->type == _DRM_AGP) map->handle = agp_remap(map->offset, map->size, dev); else map->handle = ioremap(map->offset, map->size); } -EXPORT_SYMBOL(drm_core_ioremap); +EXPORT_SYMBOL(drm_legacy_ioremap); -void drm_core_ioremap_wc(struct drm_local_map *map, struct drm_device *dev) +void drm_legacy_ioremap_wc(struct drm_local_map *map, struct drm_device *dev) { if (dev->agp && dev->agp->cant_use_aperture && map->type == _DRM_AGP) map->handle = agp_remap(map->offset, map->size, dev); else map->handle = ioremap_wc(map->offset, map->size); } -EXPORT_SYMBOL(drm_core_ioremap_wc); +EXPORT_SYMBOL(drm_legacy_ioremap_wc); -void drm_core_ioremapfree(struct drm_local_map *map, struct drm_device *dev) +void drm_legacy_ioremapfree(struct drm_local_map *map, struct drm_device *dev) { if (!map->handle || !map->size) return; @@ -136,4 +148,4 @@ void drm_core_ioremapfree(struct drm_local_map *map, struct drm_device *dev) else iounmap(map->handle); } -EXPORT_SYMBOL(drm_core_ioremapfree); +EXPORT_SYMBOL(drm_legacy_ioremapfree); diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index 6aa6a9e95570690360d191953d1566ad55227809..eb6dfe52cab24e1aa36ef12ee951c7c2d7103d6f 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -231,6 +231,9 @@ ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, const void *data, break; } + if (dsi->mode_flags & MIPI_DSI_MODE_LPM) + msg.flags = MIPI_DSI_MSG_USE_LPM; + return ops->transfer(dsi->host, &msg); } EXPORT_SYMBOL(mipi_dsi_dcs_write); @@ -260,6 +263,9 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data, if (!ops || !ops->transfer) return -ENOSYS; + if (dsi->mode_flags & MIPI_DSI_MODE_LPM) + msg.flags = MIPI_DSI_MSG_USE_LPM; + return ops->transfer(dsi->host, &msg); } EXPORT_SYMBOL(mipi_dsi_dcs_read); diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index bedf1894e17e5fa0f8f6a028a970c976725e1dba..d1b7d20065293338ccf88282369dda6a59c78c7f 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1259,6 +1259,7 @@ drm_mode_create_from_cmdline_mode(struct drm_device *dev, if (!mode) return NULL; + mode->type |= DRM_MODE_TYPE_USERDEF; drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); return mode; } diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index 3a02e5e3e9f3b58f83923776773cedd3fafa7012..474e4d12a2d8a73109a35fe3eb3b9948635fedaa 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -56,6 +56,212 @@ */ +/** + * __drm_modeset_lock_all - internal helper to grab all modeset locks + * @dev: DRM device + * @trylock: trylock mode for atomic contexts + * + * This is a special version of drm_modeset_lock_all() which can also be used in + * atomic contexts. Then @trylock must be set to true. + * + * Returns: + * 0 on success or negative error code on failure. + */ +int __drm_modeset_lock_all(struct drm_device *dev, + bool trylock) +{ + struct drm_mode_config *config = &dev->mode_config; + struct drm_modeset_acquire_ctx *ctx; + int ret; + + ctx = kzalloc(sizeof(*ctx), + trylock ? GFP_ATOMIC : GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + if (trylock) { + if (!mutex_trylock(&config->mutex)) + return -EBUSY; + } else { + mutex_lock(&config->mutex); + } + + drm_modeset_acquire_init(ctx, 0); + ctx->trylock_only = trylock; + +retry: + ret = drm_modeset_lock(&config->connection_mutex, ctx); + if (ret) + goto fail; + ret = drm_modeset_lock_all_crtcs(dev, ctx); + if (ret) + goto fail; + + WARN_ON(config->acquire_ctx); + + /* now we hold the locks, so now that it is safe, stash the + * ctx for drm_modeset_unlock_all(): + */ + config->acquire_ctx = ctx; + + drm_warn_on_modeset_not_all_locked(dev); + + return 0; + +fail: + if (ret == -EDEADLK) { + drm_modeset_backoff(ctx); + goto retry; + } + + return ret; +} +EXPORT_SYMBOL(__drm_modeset_lock_all); + +/** + * drm_modeset_lock_all - take all modeset locks + * @dev: drm device + * + * This function takes all modeset locks, suitable where a more fine-grained + * scheme isn't (yet) implemented. Locks must be dropped with + * drm_modeset_unlock_all. + */ +void drm_modeset_lock_all(struct drm_device *dev) +{ + WARN_ON(__drm_modeset_lock_all(dev, false) != 0); +} +EXPORT_SYMBOL(drm_modeset_lock_all); + +/** + * drm_modeset_unlock_all - drop all modeset locks + * @dev: device + * + * This function drop all modeset locks taken by drm_modeset_lock_all. + */ +void drm_modeset_unlock_all(struct drm_device *dev) +{ + struct drm_mode_config *config = &dev->mode_config; + struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx; + + if (WARN_ON(!ctx)) + return; + + config->acquire_ctx = NULL; + drm_modeset_drop_locks(ctx); + drm_modeset_acquire_fini(ctx); + + kfree(ctx); + + mutex_unlock(&dev->mode_config.mutex); +} +EXPORT_SYMBOL(drm_modeset_unlock_all); + +/** + * drm_modeset_lock_crtc - lock crtc with hidden acquire ctx + * @crtc: drm crtc + * + * This function locks the given crtc using a hidden acquire context. This is + * necessary so that drivers internally using the atomic interfaces can grab + * further locks with the lock acquire context. + */ +void drm_modeset_lock_crtc(struct drm_crtc *crtc) +{ + struct drm_modeset_acquire_ctx *ctx; + int ret; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (WARN_ON(!ctx)) + return; + + drm_modeset_acquire_init(ctx, 0); + +retry: + ret = drm_modeset_lock(&crtc->mutex, ctx); + if (ret) + goto fail; + + WARN_ON(crtc->acquire_ctx); + + /* now we hold the locks, so now that it is safe, stash the + * ctx for drm_modeset_unlock_crtc(): + */ + crtc->acquire_ctx = ctx; + + return; + +fail: + if (ret == -EDEADLK) { + drm_modeset_backoff(ctx); + goto retry; + } +} +EXPORT_SYMBOL(drm_modeset_lock_crtc); + +/** + * drm_modeset_legacy_acquire_ctx - find acquire ctx for legacy ioctls + * @crtc: drm crtc + * + * Legacy ioctl operations like cursor updates or page flips only have per-crtc + * locking, and store the acquire ctx in the corresponding crtc. All other + * legacy operations take all locks and use a global acquire context. This + * function grabs the right one. + */ +struct drm_modeset_acquire_ctx * +drm_modeset_legacy_acquire_ctx(struct drm_crtc *crtc) +{ + if (crtc->acquire_ctx) + return crtc->acquire_ctx; + + WARN_ON(!crtc->dev->mode_config.acquire_ctx); + + return crtc->dev->mode_config.acquire_ctx; +} +EXPORT_SYMBOL(drm_modeset_legacy_acquire_ctx); + +/** + * drm_modeset_unlock_crtc - drop crtc lock + * @crtc: drm crtc + * + * This drops the crtc lock acquire with drm_modeset_lock_crtc() and all other + * locks acquired through the hidden context. + */ +void drm_modeset_unlock_crtc(struct drm_crtc *crtc) +{ + struct drm_modeset_acquire_ctx *ctx = crtc->acquire_ctx; + + if (WARN_ON(!ctx)) + return; + + crtc->acquire_ctx = NULL; + drm_modeset_drop_locks(ctx); + drm_modeset_acquire_fini(ctx); + + kfree(ctx); +} +EXPORT_SYMBOL(drm_modeset_unlock_crtc); + +/** + * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked + * @dev: device + * + * Useful as a debug assert. + */ +void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) +{ + struct drm_crtc *crtc; + + /* Locking is currently fubar in the panic handler. */ + if (oops_in_progress) + return; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); + + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); + WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); +} +EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); + /** * drm_modeset_acquire_init - initialize acquire context * @ctx: the acquire context @@ -108,7 +314,12 @@ static inline int modeset_lock(struct drm_modeset_lock *lock, WARN_ON(ctx->contended); - if (interruptible && slow) { + if (ctx->trylock_only) { + if (!ww_mutex_trylock(&lock->mutex)) + return -EBUSY; + else + return 0; + } else if (interruptible && slow) { ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx); } else if (interruptible) { ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx); diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 020cfd9348541107774a3bcb5fd2a9ab2d9cb539..fd29f03645b8e465b42add64384b339c13229dc2 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -27,6 +27,7 @@ #include #include #include +#include "drm_legacy.h" /** * drm_pci_alloc - Allocate a PCI consistent memory block, for DMA. @@ -81,7 +82,7 @@ EXPORT_SYMBOL(drm_pci_alloc); * * This function is for internal use in the Linux-specific DRM core code. */ -void __drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) +void __drm_legacy_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) { unsigned long addr; size_t sz; @@ -105,7 +106,7 @@ void __drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) */ void drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) { - __drm_pci_free(dev, dmah); + __drm_legacy_pci_free(dev, dmah); kfree(dmah); } @@ -127,34 +128,20 @@ static int drm_get_pci_domain(struct drm_device *dev) return pci_domain_nr(dev->pdev->bus); } -static int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master) +int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master) { - int len, ret; - master->unique_len = 40; - master->unique_size = master->unique_len; - master->unique = kmalloc(master->unique_size, GFP_KERNEL); - if (master->unique == NULL) + master->unique = kasprintf(GFP_KERNEL, "pci:%04x:%02x:%02x.%d", + drm_get_pci_domain(dev), + dev->pdev->bus->number, + PCI_SLOT(dev->pdev->devfn), + PCI_FUNC(dev->pdev->devfn)); + if (!master->unique) return -ENOMEM; - - len = snprintf(master->unique, master->unique_len, - "pci:%04x:%02x:%02x.%d", - drm_get_pci_domain(dev), - dev->pdev->bus->number, - PCI_SLOT(dev->pdev->devfn), - PCI_FUNC(dev->pdev->devfn)); - - if (len >= master->unique_len) { - DRM_ERROR("buffer overflow"); - ret = -EINVAL; - goto err; - } else - master->unique_len = len; - + master->unique_len = strlen(master->unique); return 0; -err: - return ret; } +EXPORT_SYMBOL(drm_pci_set_busid); int drm_pci_set_unique(struct drm_device *dev, struct drm_master *master, @@ -163,8 +150,7 @@ int drm_pci_set_unique(struct drm_device *dev, int domain, bus, slot, func, ret; master->unique_len = u->unique_len; - master->unique_size = u->unique_len + 1; - master->unique = kmalloc(master->unique_size, GFP_KERNEL); + master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL); if (!master->unique) { ret = -ENOMEM; goto err; @@ -269,10 +255,6 @@ void drm_pci_agp_destroy(struct drm_device *dev) } } -static struct drm_bus drm_pci_bus = { - .set_busid = drm_pci_set_busid, -}; - /** * drm_get_pci_dev - Register a PCI device with the DRM subsystem * @pdev: PCI device @@ -353,8 +335,6 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) DRM_DEBUG("\n"); - driver->bus = &drm_pci_bus; - if (driver->driver_features & DRIVER_MODESET) return pci_register_driver(pdriver); diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c index d5b76f148c12aa2b214b9a4185d6e302eb3de71d..5314c9d5fef473daeb14677f08497780e87ef987 100644 --- a/drivers/gpu/drm/drm_platform.c +++ b/drivers/gpu/drm/drm_platform.c @@ -68,42 +68,23 @@ static int drm_get_platform_dev(struct platform_device *platdev, return ret; } -static int drm_platform_set_busid(struct drm_device *dev, struct drm_master *master) +int drm_platform_set_busid(struct drm_device *dev, struct drm_master *master) { - int len, ret, id; - - master->unique_len = 13 + strlen(dev->platformdev->name); - master->unique_size = master->unique_len; - master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL); - - if (master->unique == NULL) - return -ENOMEM; + int id; id = dev->platformdev->id; - - /* if only a single instance of the platform device, id will be - * set to -1.. use 0 instead to avoid a funny looking bus-id: - */ - if (id == -1) + if (id < 0) id = 0; - len = snprintf(master->unique, master->unique_len, - "platform:%s:%02d", dev->platformdev->name, id); - - if (len > master->unique_len) { - DRM_ERROR("Unique buffer overflowed\n"); - ret = -EINVAL; - goto err; - } + master->unique = kasprintf(GFP_KERNEL, "platform:%s:%02d", + dev->platformdev->name, id); + if (!master->unique) + return -ENOMEM; + master->unique_len = strlen(master->unique); return 0; -err: - return ret; } - -static struct drm_bus drm_platform_bus = { - .set_busid = drm_platform_set_busid, -}; +EXPORT_SYMBOL(drm_platform_set_busid); /** * drm_platform_init - Register a platform device with the DRM subsystem @@ -120,7 +101,6 @@ int drm_platform_init(struct drm_driver *driver, struct platform_device *platfor { DRM_DEBUG("\n"); - driver->bus = &drm_platform_bus; return drm_get_platform_dev(platform_device, driver); } EXPORT_SYMBOL(drm_platform_init); diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 99d578bad17eabc26d972fab1c4e475f9ba46096..78ca30808422fc56c67acb4659f38f06a555b1db 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -29,6 +29,9 @@ #include #include #include +#include + +#include "drm_internal.h" /* * DMA-BUF/GEM Object references and lifetime overview: @@ -522,7 +525,7 @@ struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, goto fail_detach; } - obj = dev->driver->gem_prime_import_sg_table(dev, dma_buf->size, sgt); + obj = dev->driver->gem_prime_import_sg_table(dev, attach, sgt); if (IS_ERR(obj)) { ret = PTR_ERR(obj); goto fail_unmap; diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index db7d250f7ac753dd244307d49217726e101df51e..6857e9ad6339c7a8ab94aea908be59323985df44 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -82,6 +82,22 @@ static void drm_mode_validate_flag(struct drm_connector *connector, return; } +static int drm_helper_probe_add_cmdline_mode(struct drm_connector *connector) +{ + struct drm_display_mode *mode; + + if (!connector->cmdline_mode.specified) + return 0; + + mode = drm_mode_create_from_cmdline_mode(connector->dev, + &connector->cmdline_mode); + if (mode == NULL) + return 0; + + drm_mode_probed_add(connector, mode); + return 1; +} + static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connector *connector, uint32_t maxX, uint32_t maxY, bool merge_type_bits) { @@ -141,6 +157,7 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect if (count == 0 && connector->status == connector_status_connected) count = drm_add_modes_noedid(connector, 1024, 768); + count += drm_helper_probe_add_cmdline_mode(connector); if (count == 0) goto prune; diff --git a/drivers/gpu/drm/drm_scatter.c b/drivers/gpu/drm/drm_scatter.c index 1c78406f6e71395453734a705d772303862f041e..4f0f3b36d537c258dbf28fd0dde4dd962ed98ae4 100644 --- a/drivers/gpu/drm/drm_scatter.c +++ b/drivers/gpu/drm/drm_scatter.c @@ -34,6 +34,7 @@ #include #include #include +#include "drm_legacy.h" #define DEBUG_SCATTER 0 @@ -78,8 +79,8 @@ void drm_legacy_sg_cleanup(struct drm_device *dev) # define ScatterHandle(x) (unsigned int)(x) #endif -int drm_sg_alloc(struct drm_device *dev, void *data, - struct drm_file *file_priv) +int drm_legacy_sg_alloc(struct drm_device *dev, void *data, + struct drm_file *file_priv) { struct drm_scatter_gather *request = data; struct drm_sg_mem *entry; @@ -194,8 +195,8 @@ int drm_sg_alloc(struct drm_device *dev, void *data, return -ENOMEM; } -int drm_sg_free(struct drm_device *dev, void *data, - struct drm_file *file_priv) +int drm_legacy_sg_free(struct drm_device *dev, void *data, + struct drm_file *file_priv) { struct drm_scatter_gather *request = data; struct drm_sg_mem *entry; diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index ab1a5f6dde8afd40a58987d4faa2f5f2f54828cb..cc3d6d6d67e00a52d8924bfa5dba6a51dfda5f00 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -21,6 +21,7 @@ #include #include #include +#include "drm_internal.h" #define to_drm_minor(d) dev_get_drvdata(d) #define to_drm_connector(d) dev_get_drvdata(d) diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c deleted file mode 100644 index f2fe94aab901a8edbb6cbac827bad1e27a15b242..0000000000000000000000000000000000000000 --- a/drivers/gpu/drm/drm_usb.c +++ /dev/null @@ -1,88 +0,0 @@ -#include -#include -#include -#include - -int drm_get_usb_dev(struct usb_interface *interface, - const struct usb_device_id *id, - struct drm_driver *driver) -{ - struct drm_device *dev; - int ret; - - DRM_DEBUG("\n"); - - dev = drm_dev_alloc(driver, &interface->dev); - if (!dev) - return -ENOMEM; - - dev->usbdev = interface_to_usbdev(interface); - usb_set_intfdata(interface, dev); - - ret = drm_dev_register(dev, 0); - if (ret) - goto err_free; - - DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", - driver->name, driver->major, driver->minor, driver->patchlevel, - driver->date, dev->primary->index); - - return 0; - -err_free: - drm_dev_unref(dev); - return ret; - -} -EXPORT_SYMBOL(drm_get_usb_dev); - -static int drm_usb_set_busid(struct drm_device *dev, - struct drm_master *master) -{ - return 0; -} - -static struct drm_bus drm_usb_bus = { - .set_busid = drm_usb_set_busid, -}; - -/** - * drm_usb_init - Register matching USB devices with the DRM subsystem - * @driver: DRM device driver - * @udriver: USB device driver - * - * Registers one or more devices matched by a USB driver with the DRM - * subsystem. - * - * Return: 0 on success or a negative error code on failure. - */ -int drm_usb_init(struct drm_driver *driver, struct usb_driver *udriver) -{ - int res; - DRM_DEBUG("\n"); - - driver->bus = &drm_usb_bus; - - res = usb_register(udriver); - return res; -} -EXPORT_SYMBOL(drm_usb_init); - -/** - * drm_usb_exit - Unregister matching USB devices from the DRM subsystem - * @driver: DRM device driver - * @udriver: USB device driver - * - * Unregisters one or more devices matched by a USB driver from the DRM - * subsystem. - */ -void drm_usb_exit(struct drm_driver *driver, - struct usb_driver *udriver) -{ - usb_deregister(udriver); -} -EXPORT_SYMBOL(drm_usb_exit); - -MODULE_AUTHOR("David Airlie"); -MODULE_DESCRIPTION("USB DRM support"); -MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c index 24e045c4f53140270ebbb28f9a6be21209b704a7..4a2c328959e59348ef7ee5f4bb0797835cd92ba5 100644 --- a/drivers/gpu/drm/drm_vm.c +++ b/drivers/gpu/drm/drm_vm.c @@ -35,10 +35,19 @@ #include #include +#include #if defined(__ia64__) #include #include #endif +#include +#include "drm_legacy.h" + +struct drm_vma_entry { + struct list_head head; + struct vm_area_struct *vma; + pid_t pid; +}; static void drm_vm_open(struct vm_area_struct *vma); static void drm_vm_close(struct vm_area_struct *vma); @@ -48,15 +57,11 @@ static pgprot_t drm_io_prot(struct drm_local_map *map, { pgprot_t tmp = vm_get_page_prot(vma->vm_flags); -#if defined(__i386__) || defined(__x86_64__) +#if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) if (map->type == _DRM_REGISTERS && !(map->flags & _DRM_WRITE_COMBINING)) tmp = pgprot_noncached(tmp); else tmp = pgprot_writecombine(tmp); -#elif defined(__powerpc__) - pgprot_val(tmp) |= _PAGE_NO_CACHE; - if (map->type == _DRM_REGISTERS) - pgprot_val(tmp) |= _PAGE_GUARDED; #elif defined(__ia64__) if (efi_range_is_wc(vma->vm_start, vma->vm_end - vma->vm_start)) @@ -263,7 +268,7 @@ static void drm_vm_shm_close(struct vm_area_struct *vma) dmah.vaddr = map->handle; dmah.busaddr = map->offset; dmah.size = map->size; - __drm_pci_free(dev, &dmah); + __drm_legacy_pci_free(dev, &dmah); break; } kfree(map); @@ -412,7 +417,6 @@ void drm_vm_open_locked(struct drm_device *dev, list_add(&vma_entry->head, &dev->vmalist); } } -EXPORT_SYMBOL_GPL(drm_vm_open_locked); static void drm_vm_open(struct vm_area_struct *vma) { @@ -532,7 +536,7 @@ static resource_size_t drm_core_get_reg_ofs(struct drm_device *dev) * according to the mapping type and remaps the pages. Finally sets the file * pointer and calls vm_open(). */ -int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma) +static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma) { struct drm_file *priv = filp->private_data; struct drm_device *dev = priv->minor->dev; @@ -646,7 +650,7 @@ int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma) return 0; } -int drm_mmap(struct file *filp, struct vm_area_struct *vma) +int drm_legacy_mmap(struct file *filp, struct vm_area_struct *vma) { struct drm_file *priv = filp->private_data; struct drm_device *dev = priv->minor->dev; @@ -661,4 +665,69 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma) return ret; } -EXPORT_SYMBOL(drm_mmap); +EXPORT_SYMBOL(drm_legacy_mmap); + +void drm_legacy_vma_flush(struct drm_device *dev) +{ + struct drm_vma_entry *vma, *vma_temp; + + /* Clear vma list (only needed for legacy drivers) */ + list_for_each_entry_safe(vma, vma_temp, &dev->vmalist, head) { + list_del(&vma->head); + kfree(vma); + } +} + +int drm_vma_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct drm_vma_entry *pt; + struct vm_area_struct *vma; + unsigned long vma_count = 0; +#if defined(__i386__) + unsigned int pgprot; +#endif + + mutex_lock(&dev->struct_mutex); + list_for_each_entry(pt, &dev->vmalist, head) + vma_count++; + + seq_printf(m, "vma use count: %lu, high_memory = %pK, 0x%pK\n", + vma_count, high_memory, + (void *)(unsigned long)virt_to_phys(high_memory)); + + list_for_each_entry(pt, &dev->vmalist, head) { + vma = pt->vma; + if (!vma) + continue; + seq_printf(m, + "\n%5d 0x%pK-0x%pK %c%c%c%c%c%c 0x%08lx000", + pt->pid, + (void *)vma->vm_start, (void *)vma->vm_end, + vma->vm_flags & VM_READ ? 'r' : '-', + vma->vm_flags & VM_WRITE ? 'w' : '-', + vma->vm_flags & VM_EXEC ? 'x' : '-', + vma->vm_flags & VM_MAYSHARE ? 's' : 'p', + vma->vm_flags & VM_LOCKED ? 'l' : '-', + vma->vm_flags & VM_IO ? 'i' : '-', + vma->vm_pgoff); + +#if defined(__i386__) + pgprot = pgprot_val(vma->vm_page_prot); + seq_printf(m, " %c%c%c%c%c%c%c%c%c", + pgprot & _PAGE_PRESENT ? 'p' : '-', + pgprot & _PAGE_RW ? 'w' : 'r', + pgprot & _PAGE_USER ? 'u' : 's', + pgprot & _PAGE_PWT ? 't' : 'b', + pgprot & _PAGE_PCD ? 'u' : 'c', + pgprot & _PAGE_ACCESSED ? 'a' : '-', + pgprot & _PAGE_DIRTY ? 'd' : '-', + pgprot & _PAGE_PSE ? 'm' : 'k', + pgprot & _PAGE_GLOBAL ? 'g' : 'l'); +#endif + seq_printf(m, "\n"); + } + mutex_unlock(&dev->struct_mutex); + return 0; +} diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index 4f3c7eb2d37d49a5aadc3d27ea401d3dcc430e20..cd50ece316016a403881ff7446f1ee9fdc512f9a 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -329,8 +329,8 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp) return retval; for (lane = 0; lane < lane_count; lane++) - buf[lane] = DP_TRAIN_PRE_EMPHASIS_0 | - DP_TRAIN_VOLTAGE_SWING_400; + buf[lane] = DP_TRAIN_PRE_EMPH_LEVEL_0 | + DP_TRAIN_VOLTAGE_SWING_LEVEL_0; retval = exynos_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET, lane_count, buf); @@ -937,6 +937,8 @@ static enum drm_connector_status exynos_dp_detect( static void exynos_dp_connector_destroy(struct drm_connector *connector) { + drm_connector_unregister(connector); + drm_connector_cleanup(connector); } static struct drm_connector_funcs exynos_dp_connector_funcs = { @@ -1358,8 +1360,8 @@ static void exynos_dp_unbind(struct device *dev, struct device *master, exynos_dp_dpms(display, DRM_MODE_DPMS_OFF); + exynos_dp_connector_destroy(&dp->connector); encoder->funcs->destroy(encoder); - drm_connector_cleanup(&dp->connector); } static const struct component_ops exynos_dp_ops = { diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index b68e58f78cd12f15c639b443c62a3f94431dd093..8e38e9f8e542d17d9b793ceb1efed9cb3fcc0056 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -32,7 +32,6 @@ enum exynos_crtc_mode { * Exynos specific crtc structure. * * @drm_crtc: crtc object. - * @drm_plane: pointer of private plane object for this crtc * @manager: the manager associated with this crtc * @pipe: a crtc index created at load() with a new crtc object creation * and the crtc object would be set to private->crtc array @@ -46,7 +45,6 @@ enum exynos_crtc_mode { */ struct exynos_drm_crtc { struct drm_crtc drm_crtc; - struct drm_plane *plane; struct exynos_drm_manager *manager; unsigned int pipe; unsigned int dpms; @@ -94,12 +92,12 @@ static void exynos_drm_crtc_commit(struct drm_crtc *crtc) exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON); - exynos_plane_commit(exynos_crtc->plane); + exynos_plane_commit(crtc->primary); if (manager->ops->commit) manager->ops->commit(manager); - exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON); + exynos_plane_dpms(crtc->primary, DRM_MODE_DPMS_ON); } static bool @@ -123,10 +121,9 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, { struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); struct exynos_drm_manager *manager = exynos_crtc->manager; - struct drm_plane *plane = exynos_crtc->plane; + struct drm_framebuffer *fb = crtc->primary->fb; unsigned int crtc_w; unsigned int crtc_h; - int ret; /* * copy the mode data adjusted by mode_fixup() into crtc->mode @@ -134,29 +131,21 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, */ memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode)); - crtc_w = crtc->primary->fb->width - x; - crtc_h = crtc->primary->fb->height - y; + crtc_w = fb->width - x; + crtc_h = fb->height - y; if (manager->ops->mode_set) manager->ops->mode_set(manager, &crtc->mode); - ret = exynos_plane_mode_set(plane, crtc, crtc->primary->fb, 0, 0, crtc_w, crtc_h, - x, y, crtc_w, crtc_h); - if (ret) - return ret; - - plane->crtc = crtc; - plane->fb = crtc->primary->fb; - drm_framebuffer_reference(plane->fb); - - return 0; + return exynos_plane_mode_set(crtc->primary, crtc, fb, 0, 0, + crtc_w, crtc_h, x, y, crtc_w, crtc_h); } static int exynos_drm_crtc_mode_set_commit(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - struct drm_plane *plane = exynos_crtc->plane; + struct drm_framebuffer *fb = crtc->primary->fb; unsigned int crtc_w; unsigned int crtc_h; int ret; @@ -167,11 +156,11 @@ static int exynos_drm_crtc_mode_set_commit(struct drm_crtc *crtc, int x, int y, return -EPERM; } - crtc_w = crtc->primary->fb->width - x; - crtc_h = crtc->primary->fb->height - y; + crtc_w = fb->width - x; + crtc_h = fb->height - y; - ret = exynos_plane_mode_set(plane, crtc, crtc->primary->fb, 0, 0, crtc_w, crtc_h, - x, y, crtc_w, crtc_h); + ret = exynos_plane_mode_set(crtc->primary, crtc, fb, 0, 0, + crtc_w, crtc_h, x, y, crtc_w, crtc_h); if (ret) return ret; @@ -304,8 +293,7 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc, exynos_drm_crtc_commit(crtc); break; case CRTC_MODE_BLANK: - exynos_plane_dpms(exynos_crtc->plane, - DRM_MODE_DPMS_OFF); + exynos_plane_dpms(crtc->primary, DRM_MODE_DPMS_OFF); break; default: break; @@ -351,8 +339,10 @@ static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc) int exynos_drm_crtc_create(struct exynos_drm_manager *manager) { struct exynos_drm_crtc *exynos_crtc; + struct drm_plane *plane; struct exynos_drm_private *private = manager->drm_dev->dev_private; struct drm_crtc *crtc; + int ret; exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL); if (!exynos_crtc) @@ -364,11 +354,11 @@ int exynos_drm_crtc_create(struct exynos_drm_manager *manager) exynos_crtc->dpms = DRM_MODE_DPMS_OFF; exynos_crtc->manager = manager; exynos_crtc->pipe = manager->pipe; - exynos_crtc->plane = exynos_plane_init(manager->drm_dev, - 1 << manager->pipe, true); - if (!exynos_crtc->plane) { - kfree(exynos_crtc); - return -ENOMEM; + plane = exynos_plane_init(manager->drm_dev, 1 << manager->pipe, + DRM_PLANE_TYPE_PRIMARY); + if (IS_ERR(plane)) { + ret = PTR_ERR(plane); + goto err_plane; } manager->crtc = &exynos_crtc->drm_crtc; @@ -376,12 +366,22 @@ int exynos_drm_crtc_create(struct exynos_drm_manager *manager) private->crtc[manager->pipe] = crtc; - drm_crtc_init(manager->drm_dev, crtc, &exynos_crtc_funcs); + ret = drm_crtc_init_with_planes(manager->drm_dev, crtc, plane, NULL, + &exynos_crtc_funcs); + if (ret < 0) + goto err_crtc; + drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs); exynos_drm_crtc_attach_mode_property(crtc); return 0; + +err_crtc: + plane->funcs->destroy(plane); +err_plane: + kfree(exynos_crtc); + return ret; } int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe) diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index fa08f05e3e340be433fd399127bdd821d3b198df..96c87db388fb985614e7d4485b7df569dac40784 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -342,8 +342,12 @@ int exynos_dpi_remove(struct device *dev) struct exynos_dpi *ctx = exynos_dpi_display.ctx; exynos_dpi_dpms(&exynos_dpi_display, DRM_MODE_DPMS_OFF); + + exynos_dpi_connector_destroy(&ctx->connector); encoder->funcs->destroy(encoder); - drm_connector_cleanup(&ctx->connector); + + if (ctx->panel) + drm_panel_detach(ctx->panel); exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 0d74e9b99c4ec672aa413bf6489cbb3a17898832..443a2069858a7e8589599078802281cc465de709 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -15,7 +15,6 @@ #include #include -#include #include #include @@ -86,8 +85,9 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) struct drm_plane *plane; unsigned long possible_crtcs = (1 << MAX_CRTC) - 1; - plane = exynos_plane_init(dev, possible_crtcs, false); - if (!plane) + plane = exynos_plane_init(dev, possible_crtcs, + DRM_PLANE_TYPE_OVERLAY); + if (IS_ERR(plane)) goto err_mode_config_cleanup; } @@ -116,6 +116,23 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) /* force connectors detection */ drm_helper_hpd_irq_event(dev); + /* + * enable drm irq mode. + * - with irq_enabled = true, we can use the vblank feature. + * + * P.S. note that we wouldn't use drm irq handler but + * just specific driver own one instead because + * drm framework supports only one irq handler. + */ + dev->irq_enabled = true; + + /* + * with vblank_disable_allowed = true, vblank interrupt will be disabled + * by drm timer once a current process gives up ownership of + * vblank event.(after drm_vblank_put function is called) + */ + dev->vblank_disable_allowed = true; + return 0; err_unbind_all: @@ -136,23 +153,19 @@ static int exynos_drm_unload(struct drm_device *dev) exynos_drm_device_subdrv_remove(dev); exynos_drm_fbdev_fini(dev); - drm_vblank_cleanup(dev); drm_kms_helper_poll_fini(dev); - drm_mode_config_cleanup(dev); + component_unbind_all(dev->dev, dev); + drm_vblank_cleanup(dev); + drm_mode_config_cleanup(dev); drm_release_iommu_mapping(dev); - kfree(dev->dev_private); - component_unbind_all(dev->dev, dev); + kfree(dev->dev_private); dev->dev_private = NULL; return 0; } -static const struct file_operations exynos_drm_gem_fops = { - .mmap = exynos_drm_gem_mmap_buffer, -}; - static int exynos_drm_suspend(struct drm_device *dev, pm_message_t state) { struct drm_connector *connector; @@ -191,7 +204,6 @@ static int exynos_drm_resume(struct drm_device *dev) static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) { struct drm_exynos_file_private *file_priv; - struct file *anon_filp; int ret; file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL); @@ -204,21 +216,8 @@ static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) if (ret) goto err_file_priv_free; - anon_filp = anon_inode_getfile("exynos_gem", &exynos_drm_gem_fops, - NULL, 0); - if (IS_ERR(anon_filp)) { - ret = PTR_ERR(anon_filp); - goto err_subdrv_close; - } - - anon_filp->f_mode = FMODE_READ | FMODE_WRITE; - file_priv->anon_filp = anon_filp; - return ret; -err_subdrv_close: - exynos_drm_subdrv_close(dev, file); - err_file_priv_free: kfree(file_priv); file->driver_priv = NULL; @@ -234,7 +233,6 @@ static void exynos_drm_preclose(struct drm_device *dev, static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file) { struct exynos_drm_private *private = dev->dev_private; - struct drm_exynos_file_private *file_priv; struct drm_pending_vblank_event *v, *vt; struct drm_pending_event *e, *et; unsigned long flags; @@ -260,10 +258,6 @@ static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file) } spin_unlock_irqrestore(&dev->event_lock, flags); - file_priv = file->driver_priv; - if (file_priv->anon_filp) - fput(file_priv->anon_filp); - kfree(file->driver_priv); file->driver_priv = NULL; } @@ -282,11 +276,6 @@ static const struct vm_operations_struct exynos_drm_gem_vm_ops = { static const struct drm_ioctl_desc exynos_ioctls[] = { DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl, DRM_UNLOCKED | DRM_AUTH), - DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MAP_OFFSET, - exynos_drm_gem_map_offset_ioctl, DRM_UNLOCKED | - DRM_AUTH), - DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MMAP, - exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH), DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET, exynos_drm_gem_get_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, @@ -330,6 +319,7 @@ static struct drm_driver exynos_drm_driver = { .preclose = exynos_drm_preclose, .lastclose = exynos_drm_lastclose, .postclose = exynos_drm_postclose, + .set_busid = drm_platform_set_busid, .get_vblank_counter = drm_vblank_count, .enable_vblank = exynos_drm_crtc_enable_vblank, .disable_vblank = exynos_drm_crtc_disable_vblank, @@ -485,21 +475,20 @@ void exynos_drm_component_del(struct device *dev, mutex_unlock(&drm_component_lock); } -static int compare_of(struct device *dev, void *data) +static int compare_dev(struct device *dev, void *data) { return dev == (struct device *)data; } -static int exynos_drm_add_components(struct device *dev, struct master *m) +static struct component_match *exynos_drm_match_add(struct device *dev) { + struct component_match *match = NULL; struct component_dev *cdev; unsigned int attach_cnt = 0; mutex_lock(&drm_component_lock); list_for_each_entry(cdev, &drm_component_list, list) { - int ret; - /* * Add components to master only in case that crtc and * encoder/connector device objects exist. @@ -514,16 +503,10 @@ static int exynos_drm_add_components(struct device *dev, struct master *m) /* * fimd and dpi modules have same device object so add * only crtc device object in this case. - * - * TODO. if dpi module follows driver-model driver then - * below codes can be removed. */ if (cdev->crtc_dev == cdev->conn_dev) { - ret = component_master_add_child(m, compare_of, - cdev->crtc_dev); - if (ret < 0) - return ret; - + component_match_add(dev, &match, compare_dev, + cdev->crtc_dev); goto out_lock; } @@ -533,11 +516,8 @@ static int exynos_drm_add_components(struct device *dev, struct master *m) * connector/encoder need pipe number of crtc when they * are created. */ - ret = component_master_add_child(m, compare_of, cdev->crtc_dev); - ret |= component_master_add_child(m, compare_of, - cdev->conn_dev); - if (ret < 0) - return ret; + component_match_add(dev, &match, compare_dev, cdev->crtc_dev); + component_match_add(dev, &match, compare_dev, cdev->conn_dev); out_lock: mutex_lock(&drm_component_lock); @@ -545,7 +525,7 @@ static int exynos_drm_add_components(struct device *dev, struct master *m) mutex_unlock(&drm_component_lock); - return attach_cnt ? 0 : -ENODEV; + return attach_cnt ? match : ERR_PTR(-EPROBE_DEFER); } static int exynos_drm_bind(struct device *dev) @@ -559,13 +539,13 @@ static void exynos_drm_unbind(struct device *dev) } static const struct component_master_ops exynos_drm_ops = { - .add_components = exynos_drm_add_components, .bind = exynos_drm_bind, .unbind = exynos_drm_unbind, }; static int exynos_drm_platform_probe(struct platform_device *pdev) { + struct component_match *match; int ret; pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); @@ -632,13 +612,23 @@ static int exynos_drm_platform_probe(struct platform_device *pdev) goto err_unregister_ipp_drv; #endif - ret = component_master_add(&pdev->dev, &exynos_drm_ops); + match = exynos_drm_match_add(&pdev->dev); + if (IS_ERR(match)) { + ret = PTR_ERR(match); + goto err_unregister_resources; + } + + ret = component_master_add_with_match(&pdev->dev, &exynos_drm_ops, + match); if (ret < 0) - DRM_DEBUG_KMS("re-tried by last sub driver probed later.\n"); + goto err_unregister_resources; - return 0; + return ret; + +err_unregister_resources: #ifdef CONFIG_DRM_EXYNOS_IPP + exynos_platform_device_ipp_unregister(); err_unregister_ipp_drv: platform_driver_unregister(&ipp_driver); err_unregister_gsc_drv: diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 69a6fa397d75b604174810f71a5aa0304789ac9b..d22e640f59a012c0b2e26d3d325c3a6154ec26c5 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -240,7 +240,6 @@ struct exynos_drm_g2d_private { struct drm_exynos_file_private { struct exynos_drm_g2d_private *g2d_priv; struct device *ipp_dev; - struct file *anon_filp; }; /* diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 442aa2d0013217b500ebddad0ef63429f6dd1e00..24741d8758e88521439f82309b656c35afa83785 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -114,6 +114,8 @@ #define DSIM_SYNC_INFORM (1 << 27) #define DSIM_EOT_DISABLE (1 << 28) #define DSIM_MFLUSH_VS (1 << 29) +/* This flag is valid only for exynos3250/3472/4415/5260/5430 */ +#define DSIM_CLKLANE_STOP (1 << 30) /* DSIM_ESCMODE */ #define DSIM_TX_TRIGGER_RST (1 << 4) @@ -262,6 +264,7 @@ struct exynos_dsi_driver_data { unsigned int plltmr_reg; unsigned int has_freqband:1; + unsigned int has_clklane_stop:1; }; struct exynos_dsi { @@ -301,9 +304,16 @@ struct exynos_dsi { #define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host) #define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector) +static struct exynos_dsi_driver_data exynos3_dsi_driver_data = { + .plltmr_reg = 0x50, + .has_freqband = 1, + .has_clklane_stop = 1, +}; + static struct exynos_dsi_driver_data exynos4_dsi_driver_data = { .plltmr_reg = 0x50, .has_freqband = 1, + .has_clklane_stop = 1, }; static struct exynos_dsi_driver_data exynos5_dsi_driver_data = { @@ -311,6 +321,8 @@ static struct exynos_dsi_driver_data exynos5_dsi_driver_data = { }; static struct of_device_id exynos_dsi_of_match[] = { + { .compatible = "samsung,exynos3250-mipi-dsi", + .data = &exynos3_dsi_driver_data }, { .compatible = "samsung,exynos4210-mipi-dsi", .data = &exynos4_dsi_driver_data }, { .compatible = "samsung,exynos5410-mipi-dsi", @@ -421,7 +433,7 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, if (!fout) { dev_err(dsi->dev, "failed to find PLL PMS for requested frequency\n"); - return -EFAULT; + return 0; } dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s); @@ -453,7 +465,7 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, do { if (timeout-- == 0) { dev_err(dsi->dev, "PLL failed to stabilize\n"); - return -EFAULT; + return 0; } reg = readl(dsi->reg_base + DSIM_STATUS_REG); } while ((reg & DSIM_PLL_STABLE) == 0); @@ -569,6 +581,7 @@ static void exynos_dsi_disable_clock(struct exynos_dsi *dsi) static int exynos_dsi_init_link(struct exynos_dsi *dsi) { + struct exynos_dsi_driver_data *driver_data = dsi->driver_data; int timeout; u32 reg; u32 lanes_mask; @@ -650,6 +663,20 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi) reg |= DSIM_LANE_EN(lanes_mask); writel(reg, dsi->reg_base + DSIM_CONFIG_REG); + /* + * Use non-continuous clock mode if the periparal wants and + * host controller supports + * + * In non-continous clock mode, host controller will turn off + * the HS clock between high-speed transmissions to reduce + * power consumption. + */ + if (driver_data->has_clklane_stop && + dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) { + reg |= DSIM_CLKLANE_STOP; + writel(reg, dsi->reg_base + DSIM_CONFIG_REG); + } + /* Check clock and data lane state are stop state */ timeout = 100; do { @@ -1414,6 +1441,9 @@ exynos_dsi_detect(struct drm_connector *connector, bool force) static void exynos_dsi_connector_destroy(struct drm_connector *connector) { + drm_connector_unregister(connector); + drm_connector_cleanup(connector); + connector->dev = NULL; } static struct drm_connector_funcs exynos_dsi_connector_funcs = { @@ -1634,10 +1664,10 @@ static void exynos_dsi_unbind(struct device *dev, struct device *master, exynos_dsi_dpms(&exynos_dsi_display, DRM_MODE_DPMS_OFF); - mipi_dsi_host_unregister(&dsi->dsi_host); - + exynos_dsi_connector_destroy(&dsi->connector); encoder->funcs->destroy(encoder); - drm_connector_cleanup(&dsi->connector); + + mipi_dsi_host_unregister(&dsi->dsi_host); } static const struct component_ops exynos_dsi_component_ops = { diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 65a22cad7b36300811abf39b3a0d13020a479cc0..d346d1e6eda03775e63d6ad7f969a1e1a9f4431d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -165,6 +165,7 @@ exynos_drm_framebuffer_init(struct drm_device *dev, ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs); if (ret) { + kfree(exynos_fb); DRM_ERROR("failed to initialize framebuffer\n"); return ERR_PTR(ret); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 32e63f60e1d15a14deef0d11365201e2476e5d3c..e12ea90c62371e6212fba19623c1b2034545c2f4 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -123,6 +123,7 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, fbi->screen_base = buffer->kvaddr + offset; fbi->screen_size = size; + fbi->fix.smem_len = size; return 0; } @@ -353,9 +354,6 @@ void exynos_drm_fbdev_fini(struct drm_device *dev) fbdev = to_exynos_fbdev(private->fb_helper); - if (fbdev->exynos_gem_obj) - exynos_drm_gem_destroy(fbdev->exynos_gem_obj); - exynos_drm_fbdev_destroy(dev, private->fb_helper); kfree(fbdev); private->fb_helper = NULL; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c index ec7cc9ea50df692db90293319a3f8fe2465d5107..68d38eb6774d5bb07ad2b12b0e78a6eb40dd32cd 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c @@ -336,9 +336,6 @@ static bool fimc_check_ovf(struct fimc_context *ctx) fimc_set_bits(ctx, EXYNOS_CIWDOFST, EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB | EXYNOS_CIWDOFST_CLROVFICR); - fimc_clear_bits(ctx, EXYNOS_CIWDOFST, - EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB | - EXYNOS_CIWDOFST_CLROVFICR); dev_err(ippdrv->dev, "occurred overflow at %d, status 0x%x.\n", ctx->id, status); @@ -718,24 +715,24 @@ static int fimc_src_set_addr(struct device *dev, case IPP_BUF_ENQUEUE: config = &property->config[EXYNOS_DRM_OPS_SRC]; fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_Y], - EXYNOS_CIIYSA(buf_id)); + EXYNOS_CIIYSA0); if (config->fmt == DRM_FORMAT_YVU420) { fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR], - EXYNOS_CIICBSA(buf_id)); + EXYNOS_CIICBSA0); fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB], - EXYNOS_CIICRSA(buf_id)); + EXYNOS_CIICRSA0); } else { fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB], - EXYNOS_CIICBSA(buf_id)); + EXYNOS_CIICBSA0); fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR], - EXYNOS_CIICRSA(buf_id)); + EXYNOS_CIICRSA0); } break; case IPP_BUF_DEQUEUE: - fimc_write(ctx, 0x0, EXYNOS_CIIYSA(buf_id)); - fimc_write(ctx, 0x0, EXYNOS_CIICBSA(buf_id)); - fimc_write(ctx, 0x0, EXYNOS_CIICRSA(buf_id)); + fimc_write(ctx, 0x0, EXYNOS_CIIYSA0); + fimc_write(ctx, 0x0, EXYNOS_CIICBSA0); + fimc_write(ctx, 0x0, EXYNOS_CIICRSA0); break; default: /* bypass */ @@ -1122,67 +1119,34 @@ static int fimc_dst_set_size(struct device *dev, int swap, return 0; } -static int fimc_dst_get_buf_count(struct fimc_context *ctx) -{ - u32 cfg, buf_num; - - cfg = fimc_read(ctx, EXYNOS_CIFCNTSEQ); - - buf_num = hweight32(cfg); - - DRM_DEBUG_KMS("buf_num[%d]\n", buf_num); - - return buf_num; -} - -static int fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id, +static void fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id, enum drm_exynos_ipp_buf_type buf_type) { - struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; - bool enable; - u32 cfg; - u32 mask = 0x00000001 << buf_id; - int ret = 0; unsigned long flags; + u32 buf_num; + u32 cfg; DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type); spin_lock_irqsave(&ctx->lock, flags); - /* mask register set */ cfg = fimc_read(ctx, EXYNOS_CIFCNTSEQ); - switch (buf_type) { - case IPP_BUF_ENQUEUE: - enable = true; - break; - case IPP_BUF_DEQUEUE: - enable = false; - break; - default: - dev_err(ippdrv->dev, "invalid buf ctrl parameter.\n"); - ret = -EINVAL; - goto err_unlock; - } + if (buf_type == IPP_BUF_ENQUEUE) + cfg |= (1 << buf_id); + else + cfg &= ~(1 << buf_id); - /* sequence id */ - cfg &= ~mask; - cfg |= (enable << buf_id); fimc_write(ctx, cfg, EXYNOS_CIFCNTSEQ); - /* interrupt enable */ - if (buf_type == IPP_BUF_ENQUEUE && - fimc_dst_get_buf_count(ctx) >= FIMC_BUF_START) - fimc_mask_irq(ctx, true); + buf_num = hweight32(cfg); - /* interrupt disable */ - if (buf_type == IPP_BUF_DEQUEUE && - fimc_dst_get_buf_count(ctx) <= FIMC_BUF_STOP) + if (buf_type == IPP_BUF_ENQUEUE && buf_num >= FIMC_BUF_START) + fimc_mask_irq(ctx, true); + else if (buf_type == IPP_BUF_DEQUEUE && buf_num <= FIMC_BUF_STOP) fimc_mask_irq(ctx, false); -err_unlock: spin_unlock_irqrestore(&ctx->lock, flags); - return ret; } static int fimc_dst_set_addr(struct device *dev, @@ -1240,7 +1204,9 @@ static int fimc_dst_set_addr(struct device *dev, break; } - return fimc_dst_set_buf_seq(ctx, buf_id, buf_type); + fimc_dst_set_buf_seq(ctx, buf_id, buf_type); + + return 0; } static struct exynos_drm_ipp_ops fimc_dst_ops = { @@ -1291,14 +1257,11 @@ static irqreturn_t fimc_irq_handler(int irq, void *dev_id) DRM_DEBUG_KMS("buf_id[%d]\n", buf_id); - if (fimc_dst_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE) < 0) { - DRM_ERROR("failed to dequeue.\n"); - return IRQ_HANDLED; - } + fimc_dst_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE); event_work->ippdrv = ippdrv; event_work->buf_id[EXYNOS_DRM_OPS_DST] = buf_id; - queue_work(ippdrv->event_workq, (struct work_struct *)event_work); + queue_work(ippdrv->event_workq, &event_work->work); return IRQ_HANDLED; } @@ -1590,11 +1553,8 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) fimc_clear_bits(ctx, EXYNOS_CIOCTRL, EXYNOS_CIOCTRL_WEAVE_MASK); - if (cmd == IPP_CMD_M2M) { - fimc_set_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID); - + if (cmd == IPP_CMD_M2M) fimc_set_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID); - } return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 5d09e33fef8716084a169bee2afb63e429187b48..085b066a99934116963b0aa3a1fe6d4a136387c2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -104,6 +104,14 @@ static struct fimd_driver_data s3c64xx_fimd_driver_data = { .has_limited_fmt = 1, }; +static struct fimd_driver_data exynos3_fimd_driver_data = { + .timing_base = 0x20000, + .lcdblk_offset = 0x210, + .lcdblk_bypass_shift = 1, + .has_shadowcon = 1, + .has_vidoutcon = 1, +}; + static struct fimd_driver_data exynos4_fimd_driver_data = { .timing_base = 0x0, .lcdblk_offset = 0x210, @@ -168,6 +176,8 @@ struct fimd_context { static const struct of_device_id fimd_driver_dt_match[] = { { .compatible = "samsung,s3c6400-fimd", .data = &s3c64xx_fimd_driver_data }, + { .compatible = "samsung,exynos3250-fimd", + .data = &exynos3_fimd_driver_data }, { .compatible = "samsung,exynos4210-fimd", .data = &exynos4_fimd_driver_data }, { .compatible = "samsung,exynos5250-fimd", @@ -204,7 +214,6 @@ static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr) DRM_DEBUG_KMS("vblank wait timed out.\n"); } - static void fimd_clear_channel(struct exynos_drm_manager *mgr) { struct fimd_context *ctx = mgr->ctx; @@ -214,17 +223,31 @@ static void fimd_clear_channel(struct exynos_drm_manager *mgr) /* Check if any channel is enabled. */ for (win = 0; win < WINDOWS_NR; win++) { - u32 val = readl(ctx->regs + SHADOWCON); - if (val & SHADOWCON_CHx_ENABLE(win)) { - val &= ~SHADOWCON_CHx_ENABLE(win); - writel(val, ctx->regs + SHADOWCON); + u32 val = readl(ctx->regs + WINCON(win)); + + if (val & WINCONx_ENWIN) { + /* wincon */ + val &= ~WINCONx_ENWIN; + writel(val, ctx->regs + WINCON(win)); + + /* unprotect windows */ + if (ctx->driver_data->has_shadowcon) { + val = readl(ctx->regs + SHADOWCON); + val &= ~SHADOWCON_CHx_ENABLE(win); + writel(val, ctx->regs + SHADOWCON); + } ch_enabled = 1; } } /* Wait for vsync, as disable channel takes effect at next vsync */ - if (ch_enabled) + if (ch_enabled) { + unsigned int state = ctx->suspended; + + ctx->suspended = 0; fimd_wait_for_vblank(mgr); + ctx->suspended = state; + } } static int fimd_mgr_initialize(struct exynos_drm_manager *mgr, @@ -237,23 +260,6 @@ static int fimd_mgr_initialize(struct exynos_drm_manager *mgr, mgr->drm_dev = ctx->drm_dev = drm_dev; mgr->pipe = ctx->pipe = priv->pipe++; - /* - * enable drm irq mode. - * - with irq_enabled = true, we can use the vblank feature. - * - * P.S. note that we wouldn't use drm irq handler but - * just specific driver own one instead because - * drm framework supports only one irq handler. - */ - drm_dev->irq_enabled = true; - - /* - * with vblank_disable_allowed = true, vblank interrupt will be disabled - * by drm timer once a current process gives up ownership of - * vblank event.(after drm_vblank_put function is called) - */ - drm_dev->vblank_disable_allowed = true; - /* attach this sub driver to iommu mapping if supported. */ if (is_drm_iommu_supported(ctx->drm_dev)) { /* @@ -1051,7 +1057,6 @@ static void fimd_unbind(struct device *dev, struct device *master, { struct exynos_drm_manager *mgr = dev_get_drvdata(dev); struct fimd_context *ctx = fimd_manager.ctx; - struct drm_crtc *crtc = mgr->crtc; fimd_dpms(mgr, DRM_MODE_DPMS_OFF); @@ -1059,8 +1064,6 @@ static void fimd_unbind(struct device *dev, struct device *master, exynos_dpi_remove(dev); fimd_mgr_remove(mgr); - - crtc->funcs->destroy(crtc); } static const struct component_ops fimd_component_ops = { diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 15db80138382f7d7fd58875663f0448f1312b284..0d5b9698d38402d912304b3915e95e37bd207978 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -318,40 +318,16 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev, drm_gem_object_unreference_unlocked(obj); } -int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_exynos_gem_map_off *args = data; - - DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n", - args->handle, (unsigned long)args->offset); - - if (!(dev->driver->driver_features & DRIVER_GEM)) { - DRM_ERROR("does not support GEM.\n"); - return -ENODEV; - } - - return exynos_drm_gem_dumb_map_offset(file_priv, dev, args->handle, - &args->offset); -} - -int exynos_drm_gem_mmap_buffer(struct file *filp, +int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem_obj *exynos_gem_obj, struct vm_area_struct *vma) { - struct drm_gem_object *obj = filp->private_data; - struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); - struct drm_device *drm_dev = obj->dev; + struct drm_device *drm_dev = exynos_gem_obj->base.dev; struct exynos_drm_gem_buf *buffer; unsigned long vm_size; int ret; - WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex)); - - vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; - vma->vm_private_data = obj; - vma->vm_ops = drm_dev->driver->gem_vm_ops; - - update_vm_cache_attr(exynos_gem_obj, vma); + vma->vm_flags &= ~VM_PFNMAP; + vma->vm_pgoff = 0; vm_size = vma->vm_end - vma->vm_start; @@ -373,60 +349,6 @@ int exynos_drm_gem_mmap_buffer(struct file *filp, return ret; } - /* - * take a reference to this mapping of the object. And this reference - * is unreferenced by the corresponding vm_close call. - */ - drm_gem_object_reference(obj); - - drm_vm_open_locked(drm_dev, vma); - - return 0; -} - -int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_exynos_file_private *exynos_file_priv; - struct drm_exynos_gem_mmap *args = data; - struct drm_gem_object *obj; - struct file *anon_filp; - unsigned long addr; - - if (!(dev->driver->driver_features & DRIVER_GEM)) { - DRM_ERROR("does not support GEM.\n"); - return -ENODEV; - } - - mutex_lock(&dev->struct_mutex); - - obj = drm_gem_object_lookup(dev, file_priv, args->handle); - if (!obj) { - DRM_ERROR("failed to lookup gem object.\n"); - mutex_unlock(&dev->struct_mutex); - return -EINVAL; - } - - exynos_file_priv = file_priv->driver_priv; - anon_filp = exynos_file_priv->anon_filp; - anon_filp->private_data = obj; - - addr = vm_mmap(anon_filp, 0, args->size, PROT_READ | PROT_WRITE, - MAP_SHARED, 0); - - drm_gem_object_unreference(obj); - - if (IS_ERR_VALUE(addr)) { - mutex_unlock(&dev->struct_mutex); - return (int)addr; - } - - mutex_unlock(&dev->struct_mutex); - - args->mapped = addr; - - DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped); - return 0; } @@ -710,16 +632,20 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) exynos_gem_obj = to_exynos_gem_obj(obj); ret = check_gem_flags(exynos_gem_obj->flags); - if (ret) { - drm_gem_vm_close(vma); - drm_gem_free_mmap_offset(obj); - return ret; - } - - vma->vm_flags &= ~VM_PFNMAP; - vma->vm_flags |= VM_MIXEDMAP; + if (ret) + goto err_close_vm; update_vm_cache_attr(exynos_gem_obj, vma); + ret = exynos_drm_gem_mmap_buffer(exynos_gem_obj, vma); + if (ret) + goto err_close_vm; + + return ret; + +err_close_vm: + drm_gem_vm_close(vma); + drm_gem_free_mmap_offset(obj); + return ret; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index 1592c0ba7de86b596f2000d5bb3f780fa17ca33e..ec58fe9c40df5814da0e10ebc95d6186bf6fbb2e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -12,6 +12,8 @@ #ifndef _EXYNOS_DRM_GEM_H_ #define _EXYNOS_DRM_GEM_H_ +#include + #define to_exynos_gem_obj(x) container_of(x,\ struct exynos_drm_gem_obj, base) @@ -111,20 +113,6 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev, unsigned int gem_handle, struct drm_file *filp); -/* get buffer offset to map to user space. */ -int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -/* - * mmap the physically continuous memory that a gem object contains - * to user space. - */ -int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -int exynos_drm_gem_mmap_buffer(struct file *filp, - struct vm_area_struct *vma); - /* map user space allocated by malloc to pages. */ int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c index 9e3ff1672965d3e17044d0e59fa524193404ff02..c6a013fc321cf13d9ce8b8f1a7403beb758e86e4 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c @@ -1326,8 +1326,7 @@ static irqreturn_t gsc_irq_handler(int irq, void *dev_id) buf_id[EXYNOS_DRM_OPS_SRC]; event_work->buf_id[EXYNOS_DRM_OPS_DST] = buf_id[EXYNOS_DRM_OPS_DST]; - queue_work(ippdrv->event_workq, - (struct work_struct *)event_work); + queue_work(ippdrv->event_workq, &event_work->work); } return IRQ_HANDLED; diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c index c411399070d671d1d79075230280dbf22949c267..00d74b18f7cbd841f88d252d57ca47eb5d768863 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c @@ -75,7 +75,6 @@ struct drm_exynos_ipp_mem_node { u32 prop_id; u32 buf_id; struct drm_exynos_ipp_buf_info buf_info; - struct drm_file *filp; }; /* @@ -319,44 +318,6 @@ static void ipp_print_property(struct drm_exynos_ipp_property *property, sz->hsize, sz->vsize, config->flip, config->degree); } -static int ipp_find_and_set_property(struct drm_exynos_ipp_property *property) -{ - struct exynos_drm_ippdrv *ippdrv; - struct drm_exynos_ipp_cmd_node *c_node; - u32 prop_id = property->prop_id; - - DRM_DEBUG_KMS("prop_id[%d]\n", prop_id); - - ippdrv = ipp_find_drv_by_handle(prop_id); - if (IS_ERR(ippdrv)) { - DRM_ERROR("failed to get ipp driver.\n"); - return -EINVAL; - } - - /* - * Find command node using command list in ippdrv. - * when we find this command no using prop_id. - * return property information set in this command node. - */ - mutex_lock(&ippdrv->cmd_lock); - list_for_each_entry(c_node, &ippdrv->cmd_list, list) { - if ((c_node->property.prop_id == prop_id) && - (c_node->state == IPP_STATE_STOP)) { - mutex_unlock(&ippdrv->cmd_lock); - DRM_DEBUG_KMS("found cmd[%d]ippdrv[0x%x]\n", - property->cmd, (int)ippdrv); - - c_node->property = *property; - return 0; - } - } - mutex_unlock(&ippdrv->cmd_lock); - - DRM_ERROR("failed to search property.\n"); - - return -EINVAL; -} - static struct drm_exynos_ipp_cmd_work *ipp_create_cmd_work(void) { struct drm_exynos_ipp_cmd_work *cmd_work; @@ -392,6 +353,7 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, struct drm_exynos_ipp_property *property = data; struct exynos_drm_ippdrv *ippdrv; struct drm_exynos_ipp_cmd_node *c_node; + u32 prop_id; int ret, i; if (!ctx) { @@ -404,6 +366,8 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, return -EINVAL; } + prop_id = property->prop_id; + /* * This is log print for user application property. * user application set various property. @@ -412,14 +376,24 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, ipp_print_property(property, i); /* - * set property ioctl generated new prop_id. - * but in this case already asigned prop_id using old set property. - * e.g PAUSE state. this case supports find current prop_id and use it - * instead of allocation. + * In case prop_id is not zero try to set existing property. */ - if (property->prop_id) { - DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id); - return ipp_find_and_set_property(property); + if (prop_id) { + c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock, prop_id); + + if (!c_node || c_node->filp != file) { + DRM_DEBUG_KMS("prop_id[%d] not found\n", prop_id); + return -EINVAL; + } + + if (c_node->state != IPP_STATE_STOP) { + DRM_DEBUG_KMS("prop_id[%d] not stopped\n", prop_id); + return -EINVAL; + } + + c_node->property = *property; + + return 0; } /* find ipp driver using ipp id */ @@ -445,9 +419,9 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, property->prop_id, property->cmd, (int)ippdrv); /* stored property information and ippdrv in private data */ - c_node->dev = dev; c_node->property = *property; c_node->state = IPP_STATE_IDLE; + c_node->filp = file; c_node->start_work = ipp_create_cmd_work(); if (IS_ERR(c_node->start_work)) { @@ -499,105 +473,37 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, return ret; } -static void ipp_clean_cmd_node(struct ipp_context *ctx, - struct drm_exynos_ipp_cmd_node *c_node) -{ - /* delete list */ - list_del(&c_node->list); - - ipp_remove_id(&ctx->prop_idr, &ctx->prop_lock, - c_node->property.prop_id); - - /* destroy mutex */ - mutex_destroy(&c_node->lock); - mutex_destroy(&c_node->mem_lock); - mutex_destroy(&c_node->event_lock); - - /* free command node */ - kfree(c_node->start_work); - kfree(c_node->stop_work); - kfree(c_node->event_work); - kfree(c_node); -} - -static bool ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node) -{ - switch (c_node->property.cmd) { - case IPP_CMD_WB: - return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_DST]); - case IPP_CMD_OUTPUT: - return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_SRC]); - case IPP_CMD_M2M: - default: - return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_SRC]) && - !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_DST]); - } -} - -static struct drm_exynos_ipp_mem_node - *ipp_find_mem_node(struct drm_exynos_ipp_cmd_node *c_node, - struct drm_exynos_ipp_queue_buf *qbuf) -{ - struct drm_exynos_ipp_mem_node *m_node; - struct list_head *head; - int count = 0; - - DRM_DEBUG_KMS("buf_id[%d]\n", qbuf->buf_id); - - /* source/destination memory list */ - head = &c_node->mem_list[qbuf->ops_id]; - - /* find memory node from memory list */ - list_for_each_entry(m_node, head, list) { - DRM_DEBUG_KMS("count[%d]m_node[0x%x]\n", count++, (int)m_node); - - /* compare buffer id */ - if (m_node->buf_id == qbuf->buf_id) - return m_node; - } - - return NULL; -} - -static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv, +static int ipp_put_mem_node(struct drm_device *drm_dev, struct drm_exynos_ipp_cmd_node *c_node, struct drm_exynos_ipp_mem_node *m_node) { - struct exynos_drm_ipp_ops *ops = NULL; - int ret = 0; + int i; DRM_DEBUG_KMS("node[0x%x]\n", (int)m_node); if (!m_node) { - DRM_ERROR("invalid queue node.\n"); + DRM_ERROR("invalid dequeue node.\n"); return -EFAULT; } DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id); - /* get operations callback */ - ops = ippdrv->ops[m_node->ops_id]; - if (!ops) { - DRM_ERROR("not support ops.\n"); - return -EFAULT; + /* put gem buffer */ + for_each_ipp_planar(i) { + unsigned long handle = m_node->buf_info.handles[i]; + if (handle) + exynos_drm_gem_put_dma_addr(drm_dev, handle, + c_node->filp); } - /* set address and enable irq */ - if (ops->set_addr) { - ret = ops->set_addr(ippdrv->dev, &m_node->buf_info, - m_node->buf_id, IPP_BUF_ENQUEUE); - if (ret) { - DRM_ERROR("failed to set addr.\n"); - return ret; - } - } + list_del(&m_node->list); + kfree(m_node); - return ret; + return 0; } static struct drm_exynos_ipp_mem_node *ipp_get_mem_node(struct drm_device *drm_dev, - struct drm_file *file, struct drm_exynos_ipp_cmd_node *c_node, struct drm_exynos_ipp_queue_buf *qbuf) { @@ -615,6 +521,7 @@ static struct drm_exynos_ipp_mem_node m_node->ops_id = qbuf->ops_id; m_node->prop_id = qbuf->prop_id; m_node->buf_id = qbuf->buf_id; + INIT_LIST_HEAD(&m_node->list); DRM_DEBUG_KMS("m_node[0x%x]ops_id[%d]\n", (int)m_node, qbuf->ops_id); DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]\n", qbuf->prop_id, m_node->buf_id); @@ -627,10 +534,11 @@ static struct drm_exynos_ipp_mem_node dma_addr_t *addr; addr = exynos_drm_gem_get_dma_addr(drm_dev, - qbuf->handle[i], file); + qbuf->handle[i], c_node->filp); if (IS_ERR(addr)) { DRM_ERROR("failed to get addr.\n"); - goto err_clear; + ipp_put_mem_node(drm_dev, c_node, m_node); + return ERR_PTR(-EFAULT); } buf_info->handles[i] = qbuf->handle[i]; @@ -640,46 +548,30 @@ static struct drm_exynos_ipp_mem_node } } - m_node->filp = file; mutex_lock(&c_node->mem_lock); list_add_tail(&m_node->list, &c_node->mem_list[qbuf->ops_id]); mutex_unlock(&c_node->mem_lock); return m_node; - -err_clear: - kfree(m_node); - return ERR_PTR(-EFAULT); } -static int ipp_put_mem_node(struct drm_device *drm_dev, - struct drm_exynos_ipp_cmd_node *c_node, - struct drm_exynos_ipp_mem_node *m_node) +static void ipp_clean_mem_nodes(struct drm_device *drm_dev, + struct drm_exynos_ipp_cmd_node *c_node, int ops) { - int i; - - DRM_DEBUG_KMS("node[0x%x]\n", (int)m_node); + struct drm_exynos_ipp_mem_node *m_node, *tm_node; + struct list_head *head = &c_node->mem_list[ops]; - if (!m_node) { - DRM_ERROR("invalid dequeue node.\n"); - return -EFAULT; - } + mutex_lock(&c_node->mem_lock); - DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id); + list_for_each_entry_safe(m_node, tm_node, head, list) { + int ret; - /* put gem buffer */ - for_each_ipp_planar(i) { - unsigned long handle = m_node->buf_info.handles[i]; - if (handle) - exynos_drm_gem_put_dma_addr(drm_dev, handle, - m_node->filp); + ret = ipp_put_mem_node(drm_dev, c_node, m_node); + if (ret) + DRM_ERROR("failed to put m_node.\n"); } - /* delete list in queue */ - list_del(&m_node->list); - kfree(m_node); - - return 0; + mutex_unlock(&c_node->mem_lock); } static void ipp_free_event(struct drm_pending_event *event) @@ -688,7 +580,6 @@ static void ipp_free_event(struct drm_pending_event *event) } static int ipp_get_event(struct drm_device *drm_dev, - struct drm_file *file, struct drm_exynos_ipp_cmd_node *c_node, struct drm_exynos_ipp_queue_buf *qbuf) { @@ -700,7 +591,7 @@ static int ipp_get_event(struct drm_device *drm_dev, e = kzalloc(sizeof(*e), GFP_KERNEL); if (!e) { spin_lock_irqsave(&drm_dev->event_lock, flags); - file->event_space += sizeof(e->event); + c_node->filp->event_space += sizeof(e->event); spin_unlock_irqrestore(&drm_dev->event_lock, flags); return -ENOMEM; } @@ -712,7 +603,7 @@ static int ipp_get_event(struct drm_device *drm_dev, e->event.prop_id = qbuf->prop_id; e->event.buf_id[EXYNOS_DRM_OPS_DST] = qbuf->buf_id; e->base.event = &e->event.base; - e->base.file_priv = file; + e->base.file_priv = c_node->filp; e->base.destroy = ipp_free_event; mutex_lock(&c_node->event_lock); list_add_tail(&e->base.link, &c_node->event_list); @@ -757,6 +648,115 @@ static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node, return; } +static void ipp_clean_cmd_node(struct ipp_context *ctx, + struct drm_exynos_ipp_cmd_node *c_node) +{ + int i; + + /* cancel works */ + cancel_work_sync(&c_node->start_work->work); + cancel_work_sync(&c_node->stop_work->work); + cancel_work_sync(&c_node->event_work->work); + + /* put event */ + ipp_put_event(c_node, NULL); + + for_each_ipp_ops(i) + ipp_clean_mem_nodes(ctx->subdrv.drm_dev, c_node, i); + + /* delete list */ + list_del(&c_node->list); + + ipp_remove_id(&ctx->prop_idr, &ctx->prop_lock, + c_node->property.prop_id); + + /* destroy mutex */ + mutex_destroy(&c_node->lock); + mutex_destroy(&c_node->mem_lock); + mutex_destroy(&c_node->event_lock); + + /* free command node */ + kfree(c_node->start_work); + kfree(c_node->stop_work); + kfree(c_node->event_work); + kfree(c_node); +} + +static bool ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node) +{ + switch (c_node->property.cmd) { + case IPP_CMD_WB: + return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_DST]); + case IPP_CMD_OUTPUT: + return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_SRC]); + case IPP_CMD_M2M: + default: + return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_SRC]) && + !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_DST]); + } +} + +static struct drm_exynos_ipp_mem_node + *ipp_find_mem_node(struct drm_exynos_ipp_cmd_node *c_node, + struct drm_exynos_ipp_queue_buf *qbuf) +{ + struct drm_exynos_ipp_mem_node *m_node; + struct list_head *head; + int count = 0; + + DRM_DEBUG_KMS("buf_id[%d]\n", qbuf->buf_id); + + /* source/destination memory list */ + head = &c_node->mem_list[qbuf->ops_id]; + + /* find memory node from memory list */ + list_for_each_entry(m_node, head, list) { + DRM_DEBUG_KMS("count[%d]m_node[0x%x]\n", count++, (int)m_node); + + /* compare buffer id */ + if (m_node->buf_id == qbuf->buf_id) + return m_node; + } + + return NULL; +} + +static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv, + struct drm_exynos_ipp_cmd_node *c_node, + struct drm_exynos_ipp_mem_node *m_node) +{ + struct exynos_drm_ipp_ops *ops = NULL; + int ret = 0; + + DRM_DEBUG_KMS("node[0x%x]\n", (int)m_node); + + if (!m_node) { + DRM_ERROR("invalid queue node.\n"); + return -EFAULT; + } + + DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id); + + /* get operations callback */ + ops = ippdrv->ops[m_node->ops_id]; + if (!ops) { + DRM_ERROR("not support ops.\n"); + return -EFAULT; + } + + /* set address and enable irq */ + if (ops->set_addr) { + ret = ops->set_addr(ippdrv->dev, &m_node->buf_info, + m_node->buf_id, IPP_BUF_ENQUEUE); + if (ret) { + DRM_ERROR("failed to set addr.\n"); + return ret; + } + } + + return ret; +} + static void ipp_handle_cmd_work(struct device *dev, struct exynos_drm_ippdrv *ippdrv, struct drm_exynos_ipp_cmd_work *cmd_work, @@ -766,7 +766,7 @@ static void ipp_handle_cmd_work(struct device *dev, cmd_work->ippdrv = ippdrv; cmd_work->c_node = c_node; - queue_work(ctx->cmd_workq, (struct work_struct *)cmd_work); + queue_work(ctx->cmd_workq, &cmd_work->work); } static int ipp_queue_buf_with_run(struct device *dev, @@ -872,7 +872,7 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data, /* find command node */ c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock, qbuf->prop_id); - if (!c_node) { + if (!c_node || c_node->filp != file) { DRM_ERROR("failed to get command node.\n"); return -ENODEV; } @@ -881,7 +881,7 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data, switch (qbuf->buf_type) { case IPP_BUF_ENQUEUE: /* get memory node */ - m_node = ipp_get_mem_node(drm_dev, file, c_node, qbuf); + m_node = ipp_get_mem_node(drm_dev, c_node, qbuf); if (IS_ERR(m_node)) { DRM_ERROR("failed to get m_node.\n"); return PTR_ERR(m_node); @@ -894,7 +894,7 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data, */ if (qbuf->ops_id == EXYNOS_DRM_OPS_DST) { /* get event for destination buffer */ - ret = ipp_get_event(drm_dev, file, c_node, qbuf); + ret = ipp_get_event(drm_dev, c_node, qbuf); if (ret) { DRM_ERROR("failed to get event.\n"); goto err_clean_node; @@ -1007,7 +1007,7 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data, c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock, cmd_ctrl->prop_id); - if (!c_node) { + if (!c_node || c_node->filp != file) { DRM_ERROR("invalid command node list.\n"); return -ENODEV; } @@ -1257,80 +1257,39 @@ static int ipp_stop_property(struct drm_device *drm_dev, struct exynos_drm_ippdrv *ippdrv, struct drm_exynos_ipp_cmd_node *c_node) { - struct drm_exynos_ipp_mem_node *m_node, *tm_node; struct drm_exynos_ipp_property *property = &c_node->property; - struct list_head *head; - int ret = 0, i; + int i; DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id); - /* put event */ - ipp_put_event(c_node, NULL); - - mutex_lock(&c_node->mem_lock); + /* stop operations */ + if (ippdrv->stop) + ippdrv->stop(ippdrv->dev, property->cmd); /* check command */ switch (property->cmd) { case IPP_CMD_M2M: - for_each_ipp_ops(i) { - /* source/destination memory list */ - head = &c_node->mem_list[i]; - - list_for_each_entry_safe(m_node, tm_node, - head, list) { - ret = ipp_put_mem_node(drm_dev, c_node, - m_node); - if (ret) { - DRM_ERROR("failed to put m_node.\n"); - goto err_clear; - } - } - } + for_each_ipp_ops(i) + ipp_clean_mem_nodes(drm_dev, c_node, i); break; case IPP_CMD_WB: - /* destination memory list */ - head = &c_node->mem_list[EXYNOS_DRM_OPS_DST]; - - list_for_each_entry_safe(m_node, tm_node, head, list) { - ret = ipp_put_mem_node(drm_dev, c_node, m_node); - if (ret) { - DRM_ERROR("failed to put m_node.\n"); - goto err_clear; - } - } + ipp_clean_mem_nodes(drm_dev, c_node, EXYNOS_DRM_OPS_DST); break; case IPP_CMD_OUTPUT: - /* source memory list */ - head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC]; - - list_for_each_entry_safe(m_node, tm_node, head, list) { - ret = ipp_put_mem_node(drm_dev, c_node, m_node); - if (ret) { - DRM_ERROR("failed to put m_node.\n"); - goto err_clear; - } - } + ipp_clean_mem_nodes(drm_dev, c_node, EXYNOS_DRM_OPS_SRC); break; default: DRM_ERROR("invalid operations.\n"); - ret = -EINVAL; - goto err_clear; + return -EINVAL; } -err_clear: - mutex_unlock(&c_node->mem_lock); - - /* stop operations */ - if (ippdrv->stop) - ippdrv->stop(ippdrv->dev, property->cmd); - - return ret; + return 0; } void ipp_sched_cmd(struct work_struct *work) { struct drm_exynos_ipp_cmd_work *cmd_work = - (struct drm_exynos_ipp_cmd_work *)work; + container_of(work, struct drm_exynos_ipp_cmd_work, work); struct exynos_drm_ippdrv *ippdrv; struct drm_exynos_ipp_cmd_node *c_node; struct drm_exynos_ipp_property *property; @@ -1543,7 +1502,7 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, void ipp_sched_event(struct work_struct *work) { struct drm_exynos_ipp_event_work *event_work = - (struct drm_exynos_ipp_event_work *)work; + container_of(work, struct drm_exynos_ipp_event_work, work); struct exynos_drm_ippdrv *ippdrv; struct drm_exynos_ipp_cmd_node *c_node; int ret; @@ -1646,11 +1605,11 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev) static void ipp_subdrv_remove(struct drm_device *drm_dev, struct device *dev) { - struct exynos_drm_ippdrv *ippdrv; + struct exynos_drm_ippdrv *ippdrv, *t; struct ipp_context *ctx = get_ipp_context(dev); /* get ipp driver entry */ - list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { + list_for_each_entry_safe(ippdrv, t, &exynos_drm_ippdrv_list, drv_list) { if (is_drm_iommu_supported(drm_dev)) drm_iommu_detach_device(drm_dev, ippdrv->dev); @@ -1677,14 +1636,11 @@ static int ipp_subdrv_open(struct drm_device *drm_dev, struct device *dev, static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev, struct drm_file *file) { - struct drm_exynos_file_private *file_priv = file->driver_priv; struct exynos_drm_ippdrv *ippdrv = NULL; struct ipp_context *ctx = get_ipp_context(dev); struct drm_exynos_ipp_cmd_node *c_node, *tc_node; int count = 0; - DRM_DEBUG_KMS("for priv[0x%x]\n", (int)file_priv->ipp_dev); - list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { mutex_lock(&ippdrv->cmd_lock); list_for_each_entry_safe(c_node, tc_node, @@ -1692,7 +1648,7 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev, DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n", count++, (int)ippdrv); - if (c_node->dev == file_priv->ipp_dev) { + if (c_node->filp == file) { /* * userland goto unnormal state. process killed. * and close the file. @@ -1808,63 +1764,12 @@ static int ipp_remove(struct platform_device *pdev) return 0; } -static int ipp_power_ctrl(struct ipp_context *ctx, bool enable) -{ - DRM_DEBUG_KMS("enable[%d]\n", enable); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int ipp_suspend(struct device *dev) -{ - struct ipp_context *ctx = get_ipp_context(dev); - - if (pm_runtime_suspended(dev)) - return 0; - - return ipp_power_ctrl(ctx, false); -} - -static int ipp_resume(struct device *dev) -{ - struct ipp_context *ctx = get_ipp_context(dev); - - if (!pm_runtime_suspended(dev)) - return ipp_power_ctrl(ctx, true); - - return 0; -} -#endif - -#ifdef CONFIG_PM_RUNTIME -static int ipp_runtime_suspend(struct device *dev) -{ - struct ipp_context *ctx = get_ipp_context(dev); - - return ipp_power_ctrl(ctx, false); -} - -static int ipp_runtime_resume(struct device *dev) -{ - struct ipp_context *ctx = get_ipp_context(dev); - - return ipp_power_ctrl(ctx, true); -} -#endif - -static const struct dev_pm_ops ipp_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(ipp_suspend, ipp_resume) - SET_RUNTIME_PM_OPS(ipp_runtime_suspend, ipp_runtime_resume, NULL) -}; - struct platform_driver ipp_driver = { .probe = ipp_probe, .remove = ipp_remove, .driver = { .name = "exynos-drm-ipp", .owner = THIS_MODULE, - .pm = &ipp_pm_ops, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.h b/drivers/gpu/drm/exynos/exynos_drm_ipp.h index 6f48d62aeb30742636bea0b88d2edc8442d3e247..2a61547a39d0b56b86bfabe1a0bc5fcb4b930b21 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.h +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.h @@ -48,7 +48,6 @@ struct drm_exynos_ipp_cmd_work { /* * A structure of command node. * - * @dev: IPP device. * @list: list head to command queue information. * @event_list: list head of event. * @mem_list: list head to source,destination memory queue information. @@ -62,9 +61,9 @@ struct drm_exynos_ipp_cmd_work { * @stop_work: stop command work structure. * @event_work: event work structure. * @state: state of command node. + * @filp: associated file pointer. */ struct drm_exynos_ipp_cmd_node { - struct device *dev; struct list_head list; struct list_head event_list; struct list_head mem_list[EXYNOS_DRM_OPS_MAX]; @@ -78,6 +77,7 @@ struct drm_exynos_ipp_cmd_node { struct drm_exynos_ipp_cmd_work *stop_work; struct drm_exynos_ipp_event_work *event_work; enum drm_exynos_ipp_state state; + struct drm_file *filp; }; /* diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index 8371cbd7631d421365a194c9b3d936fd8b02a567..c7045a663763f28200b4ebbb59a32b8dd874463f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -139,6 +139,8 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, overlay->crtc_x, overlay->crtc_y, overlay->crtc_width, overlay->crtc_height); + plane->crtc = crtc; + exynos_drm_crtc_plane_mode_set(crtc, overlay); return 0; @@ -187,8 +189,6 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, if (ret < 0) return ret; - plane->crtc = crtc; - exynos_plane_commit(plane); exynos_plane_dpms(plane, DRM_MODE_DPMS_ON); @@ -254,25 +254,26 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane) } struct drm_plane *exynos_plane_init(struct drm_device *dev, - unsigned long possible_crtcs, bool priv) + unsigned long possible_crtcs, + enum drm_plane_type type) { struct exynos_plane *exynos_plane; int err; exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL); if (!exynos_plane) - return NULL; + return ERR_PTR(-ENOMEM); - err = drm_plane_init(dev, &exynos_plane->base, possible_crtcs, - &exynos_plane_funcs, formats, ARRAY_SIZE(formats), - priv); + err = drm_universal_plane_init(dev, &exynos_plane->base, possible_crtcs, + &exynos_plane_funcs, formats, + ARRAY_SIZE(formats), type); if (err) { DRM_ERROR("failed to initialize plane\n"); kfree(exynos_plane); - return NULL; + return ERR_PTR(err); } - if (priv) + if (type == DRM_PLANE_TYPE_PRIMARY) exynos_plane->overlay.zpos = DEFAULT_ZPOS; else exynos_plane_attach_zpos_property(&exynos_plane->base); diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.h b/drivers/gpu/drm/exynos/exynos_drm_plane.h index 84d464c90d3d34b095459e29304fb3aec1cf6a79..0d1986b115f89543fca89953aa04930addabc215 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.h +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.h @@ -17,4 +17,5 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, void exynos_plane_commit(struct drm_plane *plane); void exynos_plane_dpms(struct drm_plane *plane, int mode); struct drm_plane *exynos_plane_init(struct drm_device *dev, - unsigned long possible_crtcs, bool priv); + unsigned long possible_crtcs, + enum drm_plane_type type); diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c index 55af6b41c1dfa3f0da4be362245212838371ff1a..b6a37d4f5b1326f9eb8f2226feea954087f6879e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_rotator.c +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c @@ -156,8 +156,7 @@ static irqreturn_t rotator_irq_handler(int irq, void *arg) event_work->ippdrv = ippdrv; event_work->buf_id[EXYNOS_DRM_OPS_DST] = rot->cur_buf_id[EXYNOS_DRM_OPS_DST]; - queue_work(ippdrv->event_workq, - (struct work_struct *)event_work); + queue_work(ippdrv->event_workq, &event_work->work); } else { DRM_ERROR("the SFR is set illegally\n"); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 9528d81d8004d70bcd9f395a68f37ab12de5d9d5..d565207040a2590d416895b5dc9247ef68824492 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -303,23 +303,6 @@ static int vidi_mgr_initialize(struct exynos_drm_manager *mgr, mgr->drm_dev = ctx->drm_dev = drm_dev; mgr->pipe = ctx->pipe = priv->pipe++; - /* - * enable drm irq mode. - * - with irq_enabled = 1, we can use the vblank feature. - * - * P.S. note that we wouldn't use drm irq handler but - * just specific driver own one instead because - * drm framework supports only one irq handler. - */ - drm_dev->irq_enabled = 1; - - /* - * with vblank_disable_allowed = 1, vblank interrupt will be disabled - * by drm timer once a current process gives up ownership of - * vblank event.(after drm_vblank_put function is called) - */ - drm_dev->vblank_disable_allowed = 1; - return 0; } @@ -648,7 +631,6 @@ static int vidi_remove(struct platform_device *pdev) struct exynos_drm_manager *mgr = platform_get_drvdata(pdev); struct vidi_context *ctx = mgr->ctx; struct drm_encoder *encoder = ctx->encoder; - struct drm_crtc *crtc = mgr->crtc; if (ctx->raw_edid != (struct edid *)fake_edid_info) { kfree(ctx->raw_edid); @@ -657,7 +639,6 @@ static int vidi_remove(struct platform_device *pdev) return -EINVAL; } - crtc->funcs->destroy(crtc); encoder->funcs->destroy(encoder); drm_connector_cleanup(&ctx->connector); diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 562966db2aa13eec90a5dbf588666675f8674892..7910fb37d9bbd6df2092f0c7147d6e0a8b944875 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1040,6 +1040,8 @@ static enum drm_connector_status hdmi_detect(struct drm_connector *connector, static void hdmi_connector_destroy(struct drm_connector *connector) { + drm_connector_unregister(connector); + drm_connector_cleanup(connector); } static struct drm_connector_funcs hdmi_connector_funcs = { @@ -2314,8 +2316,8 @@ static void hdmi_unbind(struct device *dev, struct device *master, void *data) struct drm_encoder *encoder = display->encoder; struct hdmi_context *hdata = display->ctx; + hdmi_connector_destroy(&hdata->connector); encoder->funcs->destroy(encoder); - drm_connector_cleanup(&hdata->connector); } static const struct component_ops hdmi_component_ops = { diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index e8b4ec84b312cc412351b08ab6e09f0626d5cc05..a41c84ee3a2d554a464219cf4000083b2e3fa4dd 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -1302,15 +1302,12 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data) static void mixer_unbind(struct device *dev, struct device *master, void *data) { struct exynos_drm_manager *mgr = dev_get_drvdata(dev); - struct drm_crtc *crtc = mgr->crtc; dev_info(dev, "remove successful\n"); mixer_mgr_remove(mgr); pm_runtime_disable(dev); - - crtc->funcs->destroy(crtc); } static const struct component_ops mixer_component_ops = { diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index a4cc0e60a1be732c035c06754571dafa09e4d0c8..9f158eab517a46c0f40ae8a8b37066e3a1e031cd 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -1089,7 +1089,7 @@ static char *link_train_names[] = { }; #endif -#define CDV_DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200 +#define CDV_DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_LEVEL_3 /* static uint8_t cdv_intel_dp_pre_emphasis_max(uint8_t voltage_swing) @@ -1276,7 +1276,7 @@ cdv_intel_dp_set_vswing_premph(struct gma_encoder *encoder, uint8_t signal_level cdv_sb_write(dev, ddi_reg->VSwing2, dp_vswing_premph_table[index]); /* ;gfx_dpio_set_reg(0x814c, 0x40802040) */ - if ((vswing + premph) == DP_TRAIN_VOLTAGE_SWING_1200) + if ((vswing + premph) == DP_TRAIN_VOLTAGE_SWING_LEVEL_3) cdv_sb_write(dev, ddi_reg->VSwing3, 0x70802040); else cdv_sb_write(dev, ddi_reg->VSwing3, 0x40802040); diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index d0dd3bea8aa5a1a2d98b2505139f07dc1db57715..ddd90ddbc20097d7325379d7cf77d516854d60f6 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -540,7 +540,8 @@ static void psbfb_gamma_get(struct drm_crtc *crtc, u16 *red, static int psbfb_probe(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { - struct psb_fbdev *psb_fbdev = (struct psb_fbdev *)helper; + struct psb_fbdev *psb_fbdev = + container_of(helper, struct psb_fbdev, psb_fb_helper); struct drm_device *dev = psb_fbdev->psb_fb_helper.dev; struct drm_psb_private *dev_priv = dev->dev_private; int bytespp; diff --git a/drivers/gpu/drm/gma500/gtt.h b/drivers/gpu/drm/gma500/gtt.h index f5860a739bd8551fa60868b09ab61bb74b4c0edf..cdbb350c9d5d2b23c3272ac908163e8b6610677d 100644 --- a/drivers/gpu/drm/gma500/gtt.h +++ b/drivers/gpu/drm/gma500/gtt.h @@ -21,6 +21,7 @@ #define _PSB_GTT_H_ #include +#include /* This wants cleaning up with respect to the psb_dev and un-needed stuff */ struct psb_gtt { diff --git a/drivers/gpu/drm/gma500/intel_bios.c b/drivers/gpu/drm/gma500/intel_bios.c index d3497348c4d5d81a240369d1d0dc831b30c29dbe..63bde4e86c6a11643419a099b2e3b4216a0c038b 100644 --- a/drivers/gpu/drm/gma500/intel_bios.c +++ b/drivers/gpu/drm/gma500/intel_bios.c @@ -116,30 +116,30 @@ parse_edp(struct drm_psb_private *dev_priv, struct bdb_header *bdb) switch (edp_link_params->preemphasis) { case 0: - dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_0; + dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_0; break; case 1: - dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5; + dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_1; break; case 2: - dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_6; + dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_2; break; case 3: - dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5; + dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_3; break; } switch (edp_link_params->vswing) { case 0: - dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_400; + dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_0; break; case 1: - dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_600; + dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_1; break; case 2: - dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_800; + dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_2; break; case 3: - dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_1200; + dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_3; break; } DRM_DEBUG_KMS("VBT reports EDP: VSwing %d, Preemph %d\n", diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index eec993f93b1aaea8cd7309918b2c51a08bf08725..6ec3a905fdd274bbcd1ec169efc802e8cf1663be 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -476,6 +476,7 @@ static struct drm_driver driver = { .unload = psb_driver_unload, .lastclose = psb_driver_lastclose, .preclose = psb_driver_preclose, + .set_busid = drm_pci_set_busid, .num_ioctls = ARRAY_SIZE(psb_ioctls), .device_is_agp = psb_driver_device_is_agp, diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c index bae897de9468952f882df012da4f055d53edf892..d91856779bebca20a04f198da3ce486c907927e4 100644 --- a/drivers/gpu/drm/i810/i810_dma.c +++ b/drivers/gpu/drm/i810/i810_dma.c @@ -213,7 +213,7 @@ static int i810_dma_cleanup(struct drm_device *dev) (drm_i810_private_t *) dev->dev_private; if (dev_priv->ring.virtual_start) - drm_core_ioremapfree(&dev_priv->ring.map, dev); + drm_legacy_ioremapfree(&dev_priv->ring.map, dev); if (dev_priv->hw_status_page) { pci_free_consistent(dev->pdev, PAGE_SIZE, dev_priv->hw_status_page, @@ -227,7 +227,7 @@ static int i810_dma_cleanup(struct drm_device *dev) drm_i810_buf_priv_t *buf_priv = buf->dev_private; if (buf_priv->kernel_virtual && buf->total) - drm_core_ioremapfree(&buf_priv->map, dev); + drm_legacy_ioremapfree(&buf_priv->map, dev); } } return 0; @@ -306,7 +306,7 @@ static int i810_freelist_init(struct drm_device *dev, drm_i810_private_t *dev_pr buf_priv->map.flags = 0; buf_priv->map.mtrr = 0; - drm_core_ioremap(&buf_priv->map, dev); + drm_legacy_ioremap(&buf_priv->map, dev); buf_priv->kernel_virtual = buf_priv->map.handle; } @@ -334,7 +334,7 @@ static int i810_dma_initialize(struct drm_device *dev, DRM_ERROR("can not find sarea!\n"); return -EINVAL; } - dev_priv->mmio_map = drm_core_findmap(dev, init->mmio_offset); + dev_priv->mmio_map = drm_legacy_findmap(dev, init->mmio_offset); if (!dev_priv->mmio_map) { dev->dev_private = (void *)dev_priv; i810_dma_cleanup(dev); @@ -342,7 +342,7 @@ static int i810_dma_initialize(struct drm_device *dev, return -EINVAL; } dev->agp_buffer_token = init->buffers_offset; - dev->agp_buffer_map = drm_core_findmap(dev, init->buffers_offset); + dev->agp_buffer_map = drm_legacy_findmap(dev, init->buffers_offset); if (!dev->agp_buffer_map) { dev->dev_private = (void *)dev_priv; i810_dma_cleanup(dev); @@ -363,7 +363,7 @@ static int i810_dma_initialize(struct drm_device *dev, dev_priv->ring.map.flags = 0; dev_priv->ring.map.mtrr = 0; - drm_core_ioremap(&dev_priv->ring.map, dev); + drm_legacy_ioremap(&dev_priv->ring.map, dev); if (dev_priv->ring.map.handle == NULL) { dev->dev_private = (void *)dev_priv; @@ -1215,9 +1215,9 @@ void i810_driver_preclose(struct drm_device *dev, struct drm_file *file_priv) } if (file_priv->master && file_priv->master->lock.hw_lock) { - drm_idlelock_take(&file_priv->master->lock); + drm_legacy_idlelock_take(&file_priv->master->lock); i810_driver_reclaim_buffers(dev, file_priv); - drm_idlelock_release(&file_priv->master->lock); + drm_legacy_idlelock_release(&file_priv->master->lock); } else { /* master disappeared, clean up stuff anyway and hope nothing * goes wrong */ diff --git a/drivers/gpu/drm/i810/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c index 441ccf8f5bdc3a7d2ebad9b4724eff2044d2c34b..44f4a131c8ddb4c0e68f16ecde7dbad324255edc 100644 --- a/drivers/gpu/drm/i810/i810_drv.c +++ b/drivers/gpu/drm/i810/i810_drv.c @@ -47,7 +47,7 @@ static const struct file_operations i810_driver_fops = { .open = drm_open, .release = drm_release, .unlocked_ioctl = drm_ioctl, - .mmap = drm_mmap, + .mmap = drm_legacy_mmap, .poll = drm_poll, #ifdef CONFIG_COMPAT .compat_ioctl = drm_compat_ioctl, @@ -63,6 +63,7 @@ static struct drm_driver driver = { .load = i810_driver_load, .lastclose = i810_driver_lastclose, .preclose = i810_driver_preclose, + .set_busid = drm_pci_set_busid, .device_is_agp = i810_driver_device_is_agp, .dma_quiescent = i810_driver_dma_quiescent, .ioctls = i810_ioctls, diff --git a/drivers/gpu/drm/i810/i810_drv.h b/drivers/gpu/drm/i810/i810_drv.h index d4d16eddd65110c18c557175d870c432c25d1570..93ec5dc4e7d3034743bf03f32923f8c3db932abc 100644 --- a/drivers/gpu/drm/i810/i810_drv.h +++ b/drivers/gpu/drm/i810/i810_drv.h @@ -32,6 +32,8 @@ #ifndef _I810_DRV_H_ #define _I810_DRV_H_ +#include + /* General customization: */ diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 91bd167e1cb70322998d92050d01f43eae7d0888..c1dd485aeb6c255230c83e7fea876fb79fed3308 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -31,6 +31,7 @@ i915-y += i915_cmd_parser.o \ i915_gpu_error.o \ i915_irq.o \ i915_trace_points.o \ + intel_lrc.o \ intel_ringbuffer.o \ intel_uncore.o diff --git a/drivers/gpu/drm/i915/dvo_ns2501.c b/drivers/gpu/drm/i915/dvo_ns2501.c index 74f2af7c2d3ed41688494a41b78e28390cf7c2b5..441630434d34742b38d938e5ba6bee0fa6faa965 100644 --- a/drivers/gpu/drm/i915/dvo_ns2501.c +++ b/drivers/gpu/drm/i915/dvo_ns2501.c @@ -60,16 +60,297 @@ #define NS2501_REGC 0x0c +enum { + MODE_640x480, + MODE_800x600, + MODE_1024x768, +}; + +struct ns2501_reg { + uint8_t offset; + uint8_t value; +}; + +/* + * Magic values based on what the BIOS on + * Fujitsu-Siemens Lifebook S6010 programs (1024x768 panel). + */ +static const struct ns2501_reg regs_1024x768[][86] = { + [MODE_640x480] = { + [0] = { .offset = 0x0a, .value = 0x81, }, + [1] = { .offset = 0x18, .value = 0x07, }, + [2] = { .offset = 0x19, .value = 0x00, }, + [3] = { .offset = 0x1a, .value = 0x00, }, + [4] = { .offset = 0x1b, .value = 0x11, }, + [5] = { .offset = 0x1c, .value = 0x54, }, + [6] = { .offset = 0x1d, .value = 0x03, }, + [7] = { .offset = 0x1e, .value = 0x02, }, + [8] = { .offset = 0xf3, .value = 0x90, }, + [9] = { .offset = 0xf9, .value = 0x00, }, + [10] = { .offset = 0xc1, .value = 0x90, }, + [11] = { .offset = 0xc2, .value = 0x00, }, + [12] = { .offset = 0xc3, .value = 0x0f, }, + [13] = { .offset = 0xc4, .value = 0x03, }, + [14] = { .offset = 0xc5, .value = 0x16, }, + [15] = { .offset = 0xc6, .value = 0x00, }, + [16] = { .offset = 0xc7, .value = 0x02, }, + [17] = { .offset = 0xc8, .value = 0x02, }, + [18] = { .offset = 0xf4, .value = 0x00, }, + [19] = { .offset = 0x80, .value = 0xff, }, + [20] = { .offset = 0x81, .value = 0x07, }, + [21] = { .offset = 0x82, .value = 0x3d, }, + [22] = { .offset = 0x83, .value = 0x05, }, + [23] = { .offset = 0x94, .value = 0x00, }, + [24] = { .offset = 0x95, .value = 0x00, }, + [25] = { .offset = 0x96, .value = 0x05, }, + [26] = { .offset = 0x97, .value = 0x00, }, + [27] = { .offset = 0x9a, .value = 0x88, }, + [28] = { .offset = 0x9b, .value = 0x00, }, + [29] = { .offset = 0x98, .value = 0x00, }, + [30] = { .offset = 0x99, .value = 0x00, }, + [31] = { .offset = 0xf7, .value = 0x88, }, + [32] = { .offset = 0xf8, .value = 0x0a, }, + [33] = { .offset = 0x9c, .value = 0x24, }, + [34] = { .offset = 0x9d, .value = 0x00, }, + [35] = { .offset = 0x9e, .value = 0x25, }, + [36] = { .offset = 0x9f, .value = 0x03, }, + [37] = { .offset = 0xa0, .value = 0x28, }, + [38] = { .offset = 0xa1, .value = 0x01, }, + [39] = { .offset = 0xa2, .value = 0x28, }, + [40] = { .offset = 0xa3, .value = 0x05, }, + [41] = { .offset = 0xb6, .value = 0x09, }, + [42] = { .offset = 0xb8, .value = 0x00, }, + [43] = { .offset = 0xb9, .value = 0xa0, }, + [44] = { .offset = 0xba, .value = 0x00, }, + [45] = { .offset = 0xbb, .value = 0x20, }, + [46] = { .offset = 0x10, .value = 0x00, }, + [47] = { .offset = 0x11, .value = 0xa0, }, + [48] = { .offset = 0x12, .value = 0x02, }, + [49] = { .offset = 0x20, .value = 0x00, }, + [50] = { .offset = 0x22, .value = 0x00, }, + [51] = { .offset = 0x23, .value = 0x00, }, + [52] = { .offset = 0x24, .value = 0x00, }, + [53] = { .offset = 0x25, .value = 0x00, }, + [54] = { .offset = 0x8c, .value = 0x10, }, + [55] = { .offset = 0x8d, .value = 0x02, }, + [56] = { .offset = 0x8e, .value = 0x10, }, + [57] = { .offset = 0x8f, .value = 0x00, }, + [58] = { .offset = 0x90, .value = 0xff, }, + [59] = { .offset = 0x91, .value = 0x07, }, + [60] = { .offset = 0x92, .value = 0xa0, }, + [61] = { .offset = 0x93, .value = 0x02, }, + [62] = { .offset = 0xa5, .value = 0x00, }, + [63] = { .offset = 0xa6, .value = 0x00, }, + [64] = { .offset = 0xa7, .value = 0x00, }, + [65] = { .offset = 0xa8, .value = 0x00, }, + [66] = { .offset = 0xa9, .value = 0x04, }, + [67] = { .offset = 0xaa, .value = 0x70, }, + [68] = { .offset = 0xab, .value = 0x4f, }, + [69] = { .offset = 0xac, .value = 0x00, }, + [70] = { .offset = 0xa4, .value = 0x84, }, + [71] = { .offset = 0x7e, .value = 0x18, }, + [72] = { .offset = 0x84, .value = 0x00, }, + [73] = { .offset = 0x85, .value = 0x00, }, + [74] = { .offset = 0x86, .value = 0x00, }, + [75] = { .offset = 0x87, .value = 0x00, }, + [76] = { .offset = 0x88, .value = 0x00, }, + [77] = { .offset = 0x89, .value = 0x00, }, + [78] = { .offset = 0x8a, .value = 0x00, }, + [79] = { .offset = 0x8b, .value = 0x00, }, + [80] = { .offset = 0x26, .value = 0x00, }, + [81] = { .offset = 0x27, .value = 0x00, }, + [82] = { .offset = 0xad, .value = 0x00, }, + [83] = { .offset = 0x08, .value = 0x30, }, /* 0x31 */ + [84] = { .offset = 0x41, .value = 0x00, }, + [85] = { .offset = 0xc0, .value = 0x05, }, + }, + [MODE_800x600] = { + [0] = { .offset = 0x0a, .value = 0x81, }, + [1] = { .offset = 0x18, .value = 0x07, }, + [2] = { .offset = 0x19, .value = 0x00, }, + [3] = { .offset = 0x1a, .value = 0x00, }, + [4] = { .offset = 0x1b, .value = 0x19, }, + [5] = { .offset = 0x1c, .value = 0x64, }, + [6] = { .offset = 0x1d, .value = 0x02, }, + [7] = { .offset = 0x1e, .value = 0x02, }, + [8] = { .offset = 0xf3, .value = 0x90, }, + [9] = { .offset = 0xf9, .value = 0x00, }, + [10] = { .offset = 0xc1, .value = 0xd7, }, + [11] = { .offset = 0xc2, .value = 0x00, }, + [12] = { .offset = 0xc3, .value = 0xf8, }, + [13] = { .offset = 0xc4, .value = 0x03, }, + [14] = { .offset = 0xc5, .value = 0x1a, }, + [15] = { .offset = 0xc6, .value = 0x00, }, + [16] = { .offset = 0xc7, .value = 0x73, }, + [17] = { .offset = 0xc8, .value = 0x02, }, + [18] = { .offset = 0xf4, .value = 0x00, }, + [19] = { .offset = 0x80, .value = 0x27, }, + [20] = { .offset = 0x81, .value = 0x03, }, + [21] = { .offset = 0x82, .value = 0x41, }, + [22] = { .offset = 0x83, .value = 0x05, }, + [23] = { .offset = 0x94, .value = 0x00, }, + [24] = { .offset = 0x95, .value = 0x00, }, + [25] = { .offset = 0x96, .value = 0x05, }, + [26] = { .offset = 0x97, .value = 0x00, }, + [27] = { .offset = 0x9a, .value = 0x88, }, + [28] = { .offset = 0x9b, .value = 0x00, }, + [29] = { .offset = 0x98, .value = 0x00, }, + [30] = { .offset = 0x99, .value = 0x00, }, + [31] = { .offset = 0xf7, .value = 0x88, }, + [32] = { .offset = 0xf8, .value = 0x06, }, + [33] = { .offset = 0x9c, .value = 0x23, }, + [34] = { .offset = 0x9d, .value = 0x00, }, + [35] = { .offset = 0x9e, .value = 0x25, }, + [36] = { .offset = 0x9f, .value = 0x03, }, + [37] = { .offset = 0xa0, .value = 0x28, }, + [38] = { .offset = 0xa1, .value = 0x01, }, + [39] = { .offset = 0xa2, .value = 0x28, }, + [40] = { .offset = 0xa3, .value = 0x05, }, + [41] = { .offset = 0xb6, .value = 0x09, }, + [42] = { .offset = 0xb8, .value = 0x30, }, + [43] = { .offset = 0xb9, .value = 0xc8, }, + [44] = { .offset = 0xba, .value = 0x00, }, + [45] = { .offset = 0xbb, .value = 0x20, }, + [46] = { .offset = 0x10, .value = 0x20, }, + [47] = { .offset = 0x11, .value = 0xc8, }, + [48] = { .offset = 0x12, .value = 0x02, }, + [49] = { .offset = 0x20, .value = 0x00, }, + [50] = { .offset = 0x22, .value = 0x00, }, + [51] = { .offset = 0x23, .value = 0x00, }, + [52] = { .offset = 0x24, .value = 0x00, }, + [53] = { .offset = 0x25, .value = 0x00, }, + [54] = { .offset = 0x8c, .value = 0x10, }, + [55] = { .offset = 0x8d, .value = 0x02, }, + [56] = { .offset = 0x8e, .value = 0x04, }, + [57] = { .offset = 0x8f, .value = 0x00, }, + [58] = { .offset = 0x90, .value = 0xff, }, + [59] = { .offset = 0x91, .value = 0x07, }, + [60] = { .offset = 0x92, .value = 0xa0, }, + [61] = { .offset = 0x93, .value = 0x02, }, + [62] = { .offset = 0xa5, .value = 0x00, }, + [63] = { .offset = 0xa6, .value = 0x00, }, + [64] = { .offset = 0xa7, .value = 0x00, }, + [65] = { .offset = 0xa8, .value = 0x00, }, + [66] = { .offset = 0xa9, .value = 0x83, }, + [67] = { .offset = 0xaa, .value = 0x40, }, + [68] = { .offset = 0xab, .value = 0x32, }, + [69] = { .offset = 0xac, .value = 0x00, }, + [70] = { .offset = 0xa4, .value = 0x80, }, + [71] = { .offset = 0x7e, .value = 0x18, }, + [72] = { .offset = 0x84, .value = 0x00, }, + [73] = { .offset = 0x85, .value = 0x00, }, + [74] = { .offset = 0x86, .value = 0x00, }, + [75] = { .offset = 0x87, .value = 0x00, }, + [76] = { .offset = 0x88, .value = 0x00, }, + [77] = { .offset = 0x89, .value = 0x00, }, + [78] = { .offset = 0x8a, .value = 0x00, }, + [79] = { .offset = 0x8b, .value = 0x00, }, + [80] = { .offset = 0x26, .value = 0x00, }, + [81] = { .offset = 0x27, .value = 0x00, }, + [82] = { .offset = 0xad, .value = 0x00, }, + [83] = { .offset = 0x08, .value = 0x30, }, /* 0x31 */ + [84] = { .offset = 0x41, .value = 0x00, }, + [85] = { .offset = 0xc0, .value = 0x07, }, + }, + [MODE_1024x768] = { + [0] = { .offset = 0x0a, .value = 0x81, }, + [1] = { .offset = 0x18, .value = 0x07, }, + [2] = { .offset = 0x19, .value = 0x00, }, + [3] = { .offset = 0x1a, .value = 0x00, }, + [4] = { .offset = 0x1b, .value = 0x11, }, + [5] = { .offset = 0x1c, .value = 0x54, }, + [6] = { .offset = 0x1d, .value = 0x03, }, + [7] = { .offset = 0x1e, .value = 0x02, }, + [8] = { .offset = 0xf3, .value = 0x90, }, + [9] = { .offset = 0xf9, .value = 0x00, }, + [10] = { .offset = 0xc1, .value = 0x90, }, + [11] = { .offset = 0xc2, .value = 0x00, }, + [12] = { .offset = 0xc3, .value = 0x0f, }, + [13] = { .offset = 0xc4, .value = 0x03, }, + [14] = { .offset = 0xc5, .value = 0x16, }, + [15] = { .offset = 0xc6, .value = 0x00, }, + [16] = { .offset = 0xc7, .value = 0x02, }, + [17] = { .offset = 0xc8, .value = 0x02, }, + [18] = { .offset = 0xf4, .value = 0x00, }, + [19] = { .offset = 0x80, .value = 0xff, }, + [20] = { .offset = 0x81, .value = 0x07, }, + [21] = { .offset = 0x82, .value = 0x3d, }, + [22] = { .offset = 0x83, .value = 0x05, }, + [23] = { .offset = 0x94, .value = 0x00, }, + [24] = { .offset = 0x95, .value = 0x00, }, + [25] = { .offset = 0x96, .value = 0x05, }, + [26] = { .offset = 0x97, .value = 0x00, }, + [27] = { .offset = 0x9a, .value = 0x88, }, + [28] = { .offset = 0x9b, .value = 0x00, }, + [29] = { .offset = 0x98, .value = 0x00, }, + [30] = { .offset = 0x99, .value = 0x00, }, + [31] = { .offset = 0xf7, .value = 0x88, }, + [32] = { .offset = 0xf8, .value = 0x0a, }, + [33] = { .offset = 0x9c, .value = 0x24, }, + [34] = { .offset = 0x9d, .value = 0x00, }, + [35] = { .offset = 0x9e, .value = 0x25, }, + [36] = { .offset = 0x9f, .value = 0x03, }, + [37] = { .offset = 0xa0, .value = 0x28, }, + [38] = { .offset = 0xa1, .value = 0x01, }, + [39] = { .offset = 0xa2, .value = 0x28, }, + [40] = { .offset = 0xa3, .value = 0x05, }, + [41] = { .offset = 0xb6, .value = 0x09, }, + [42] = { .offset = 0xb8, .value = 0x00, }, + [43] = { .offset = 0xb9, .value = 0xa0, }, + [44] = { .offset = 0xba, .value = 0x00, }, + [45] = { .offset = 0xbb, .value = 0x20, }, + [46] = { .offset = 0x10, .value = 0x00, }, + [47] = { .offset = 0x11, .value = 0xa0, }, + [48] = { .offset = 0x12, .value = 0x02, }, + [49] = { .offset = 0x20, .value = 0x00, }, + [50] = { .offset = 0x22, .value = 0x00, }, + [51] = { .offset = 0x23, .value = 0x00, }, + [52] = { .offset = 0x24, .value = 0x00, }, + [53] = { .offset = 0x25, .value = 0x00, }, + [54] = { .offset = 0x8c, .value = 0x10, }, + [55] = { .offset = 0x8d, .value = 0x02, }, + [56] = { .offset = 0x8e, .value = 0x10, }, + [57] = { .offset = 0x8f, .value = 0x00, }, + [58] = { .offset = 0x90, .value = 0xff, }, + [59] = { .offset = 0x91, .value = 0x07, }, + [60] = { .offset = 0x92, .value = 0xa0, }, + [61] = { .offset = 0x93, .value = 0x02, }, + [62] = { .offset = 0xa5, .value = 0x00, }, + [63] = { .offset = 0xa6, .value = 0x00, }, + [64] = { .offset = 0xa7, .value = 0x00, }, + [65] = { .offset = 0xa8, .value = 0x00, }, + [66] = { .offset = 0xa9, .value = 0x04, }, + [67] = { .offset = 0xaa, .value = 0x70, }, + [68] = { .offset = 0xab, .value = 0x4f, }, + [69] = { .offset = 0xac, .value = 0x00, }, + [70] = { .offset = 0xa4, .value = 0x84, }, + [71] = { .offset = 0x7e, .value = 0x18, }, + [72] = { .offset = 0x84, .value = 0x00, }, + [73] = { .offset = 0x85, .value = 0x00, }, + [74] = { .offset = 0x86, .value = 0x00, }, + [75] = { .offset = 0x87, .value = 0x00, }, + [76] = { .offset = 0x88, .value = 0x00, }, + [77] = { .offset = 0x89, .value = 0x00, }, + [78] = { .offset = 0x8a, .value = 0x00, }, + [79] = { .offset = 0x8b, .value = 0x00, }, + [80] = { .offset = 0x26, .value = 0x00, }, + [81] = { .offset = 0x27, .value = 0x00, }, + [82] = { .offset = 0xad, .value = 0x00, }, + [83] = { .offset = 0x08, .value = 0x34, }, /* 0x35 */ + [84] = { .offset = 0x41, .value = 0x00, }, + [85] = { .offset = 0xc0, .value = 0x01, }, + }, +}; + +static const struct ns2501_reg regs_init[] = { + [0] = { .offset = 0x35, .value = 0xff, }, + [1] = { .offset = 0x34, .value = 0x00, }, + [2] = { .offset = 0x08, .value = 0x30, }, +}; + struct ns2501_priv { - //I2CDevRec d; bool quiet; - int reg_8_shadow; - int reg_8_set; - // Shadow registers for i915 - int dvoc; - int pll_a; - int srcdim; - int fw_blc; + const struct ns2501_reg *regs; }; #define NSPTR(d) ((NS2501Ptr)(d->DriverPrivate.ptr)) @@ -205,11 +486,9 @@ static bool ns2501_init(struct intel_dvo_device *dvo, goto out; } ns->quiet = false; - ns->reg_8_set = 0; - ns->reg_8_shadow = - NS2501_8_PD | NS2501_8_BPAS | NS2501_8_VEN | NS2501_8_HEN; DRM_DEBUG_KMS("init ns2501 dvo controller successfully!\n"); + return true; out: @@ -242,9 +521,9 @@ static enum drm_mode_status ns2501_mode_valid(struct intel_dvo_device *dvo, * of the panel in here so we could always accept it * by disabling the scaler. */ - if ((mode->hdisplay == 800 && mode->vdisplay == 600) || - (mode->hdisplay == 640 && mode->vdisplay == 480) || - (mode->hdisplay == 1024 && mode->vdisplay == 768)) { + if ((mode->hdisplay == 640 && mode->vdisplay == 480 && mode->clock == 25175) || + (mode->hdisplay == 800 && mode->vdisplay == 600 && mode->clock == 40000) || + (mode->hdisplay == 1024 && mode->vdisplay == 768 && mode->clock == 65000)) { return MODE_OK; } else { return MODE_ONE_SIZE; /* Is this a reasonable error? */ @@ -255,180 +534,30 @@ static void ns2501_mode_set(struct intel_dvo_device *dvo, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - bool ok; - int retries = 10; struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv); + int mode_idx, i; DRM_DEBUG_KMS ("set mode (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d).\n", mode->hdisplay, mode->htotal, mode->vdisplay, mode->vtotal); - /* - * Where do I find the native resolution for which scaling is not required??? - * - * First trigger the DVO on as otherwise the chip does not appear on the i2c - * bus. - */ - do { - ok = true; - - if (mode->hdisplay == 800 && mode->vdisplay == 600) { - /* mode 277 */ - ns->reg_8_shadow &= ~NS2501_8_BPAS; - DRM_DEBUG_KMS("switching to 800x600\n"); - - /* - * No, I do not know where this data comes from. - * It is just what the video bios left in the DVO, so - * I'm just copying it here over. - * This also means that I cannot support any other modes - * except the ones supported by the bios. - */ - ok &= ns2501_writeb(dvo, 0x11, 0xc8); // 0xc7 also works. - ok &= ns2501_writeb(dvo, 0x1b, 0x19); - ok &= ns2501_writeb(dvo, 0x1c, 0x62); // VBIOS left 0x64 here, but 0x62 works nicer - ok &= ns2501_writeb(dvo, 0x1d, 0x02); - - ok &= ns2501_writeb(dvo, 0x34, 0x03); - ok &= ns2501_writeb(dvo, 0x35, 0xff); + if (mode->hdisplay == 640 && mode->vdisplay == 480) + mode_idx = MODE_640x480; + else if (mode->hdisplay == 800 && mode->vdisplay == 600) + mode_idx = MODE_800x600; + else if (mode->hdisplay == 1024 && mode->vdisplay == 768) + mode_idx = MODE_1024x768; + else + return; - ok &= ns2501_writeb(dvo, 0x80, 0x27); - ok &= ns2501_writeb(dvo, 0x81, 0x03); - ok &= ns2501_writeb(dvo, 0x82, 0x41); - ok &= ns2501_writeb(dvo, 0x83, 0x05); + /* Hopefully doing it every time won't hurt... */ + for (i = 0; i < ARRAY_SIZE(regs_init); i++) + ns2501_writeb(dvo, regs_init[i].offset, regs_init[i].value); - ok &= ns2501_writeb(dvo, 0x8d, 0x02); - ok &= ns2501_writeb(dvo, 0x8e, 0x04); - ok &= ns2501_writeb(dvo, 0x8f, 0x00); + ns->regs = regs_1024x768[mode_idx]; - ok &= ns2501_writeb(dvo, 0x90, 0xfe); /* vertical. VBIOS left 0xff here, but 0xfe works better */ - ok &= ns2501_writeb(dvo, 0x91, 0x07); - ok &= ns2501_writeb(dvo, 0x94, 0x00); - ok &= ns2501_writeb(dvo, 0x95, 0x00); - - ok &= ns2501_writeb(dvo, 0x96, 0x00); - - ok &= ns2501_writeb(dvo, 0x99, 0x00); - ok &= ns2501_writeb(dvo, 0x9a, 0x88); - - ok &= ns2501_writeb(dvo, 0x9c, 0x23); /* Looks like first and last line of the image. */ - ok &= ns2501_writeb(dvo, 0x9d, 0x00); - ok &= ns2501_writeb(dvo, 0x9e, 0x25); - ok &= ns2501_writeb(dvo, 0x9f, 0x03); - - ok &= ns2501_writeb(dvo, 0xa4, 0x80); - - ok &= ns2501_writeb(dvo, 0xb6, 0x00); - - ok &= ns2501_writeb(dvo, 0xb9, 0xc8); /* horizontal? */ - ok &= ns2501_writeb(dvo, 0xba, 0x00); /* horizontal? */ - - ok &= ns2501_writeb(dvo, 0xc0, 0x05); /* horizontal? */ - ok &= ns2501_writeb(dvo, 0xc1, 0xd7); - - ok &= ns2501_writeb(dvo, 0xc2, 0x00); - ok &= ns2501_writeb(dvo, 0xc3, 0xf8); - - ok &= ns2501_writeb(dvo, 0xc4, 0x03); - ok &= ns2501_writeb(dvo, 0xc5, 0x1a); - - ok &= ns2501_writeb(dvo, 0xc6, 0x00); - ok &= ns2501_writeb(dvo, 0xc7, 0x73); - ok &= ns2501_writeb(dvo, 0xc8, 0x02); - - } else if (mode->hdisplay == 640 && mode->vdisplay == 480) { - /* mode 274 */ - DRM_DEBUG_KMS("switching to 640x480\n"); - /* - * No, I do not know where this data comes from. - * It is just what the video bios left in the DVO, so - * I'm just copying it here over. - * This also means that I cannot support any other modes - * except the ones supported by the bios. - */ - ns->reg_8_shadow &= ~NS2501_8_BPAS; - - ok &= ns2501_writeb(dvo, 0x11, 0xa0); - ok &= ns2501_writeb(dvo, 0x1b, 0x11); - ok &= ns2501_writeb(dvo, 0x1c, 0x54); - ok &= ns2501_writeb(dvo, 0x1d, 0x03); - - ok &= ns2501_writeb(dvo, 0x34, 0x03); - ok &= ns2501_writeb(dvo, 0x35, 0xff); - - ok &= ns2501_writeb(dvo, 0x80, 0xff); - ok &= ns2501_writeb(dvo, 0x81, 0x07); - ok &= ns2501_writeb(dvo, 0x82, 0x3d); - ok &= ns2501_writeb(dvo, 0x83, 0x05); - - ok &= ns2501_writeb(dvo, 0x8d, 0x02); - ok &= ns2501_writeb(dvo, 0x8e, 0x10); - ok &= ns2501_writeb(dvo, 0x8f, 0x00); - - ok &= ns2501_writeb(dvo, 0x90, 0xff); /* vertical */ - ok &= ns2501_writeb(dvo, 0x91, 0x07); - ok &= ns2501_writeb(dvo, 0x94, 0x00); - ok &= ns2501_writeb(dvo, 0x95, 0x00); - - ok &= ns2501_writeb(dvo, 0x96, 0x05); - - ok &= ns2501_writeb(dvo, 0x99, 0x00); - ok &= ns2501_writeb(dvo, 0x9a, 0x88); - - ok &= ns2501_writeb(dvo, 0x9c, 0x24); - ok &= ns2501_writeb(dvo, 0x9d, 0x00); - ok &= ns2501_writeb(dvo, 0x9e, 0x25); - ok &= ns2501_writeb(dvo, 0x9f, 0x03); - - ok &= ns2501_writeb(dvo, 0xa4, 0x84); - - ok &= ns2501_writeb(dvo, 0xb6, 0x09); - - ok &= ns2501_writeb(dvo, 0xb9, 0xa0); /* horizontal? */ - ok &= ns2501_writeb(dvo, 0xba, 0x00); /* horizontal? */ - - ok &= ns2501_writeb(dvo, 0xc0, 0x05); /* horizontal? */ - ok &= ns2501_writeb(dvo, 0xc1, 0x90); - - ok &= ns2501_writeb(dvo, 0xc2, 0x00); - ok &= ns2501_writeb(dvo, 0xc3, 0x0f); - - ok &= ns2501_writeb(dvo, 0xc4, 0x03); - ok &= ns2501_writeb(dvo, 0xc5, 0x16); - - ok &= ns2501_writeb(dvo, 0xc6, 0x00); - ok &= ns2501_writeb(dvo, 0xc7, 0x02); - ok &= ns2501_writeb(dvo, 0xc8, 0x02); - - } else if (mode->hdisplay == 1024 && mode->vdisplay == 768) { - /* mode 280 */ - DRM_DEBUG_KMS("switching to 1024x768\n"); - /* - * This might or might not work, actually. I'm silently - * assuming here that the native panel resolution is - * 1024x768. If not, then this leaves the scaler disabled - * generating a picture that is likely not the expected. - * - * Problem is that I do not know where to take the panel - * dimensions from. - * - * Enable the bypass, scaling not required. - * - * The scaler registers are irrelevant here.... - * - */ - ns->reg_8_shadow |= NS2501_8_BPAS; - ok &= ns2501_writeb(dvo, 0x37, 0x44); - } else { - /* - * Data not known. Bummer! - * Hopefully, the code should not go here - * as mode_OK delivered no other modes. - */ - ns->reg_8_shadow |= NS2501_8_BPAS; - } - ok &= ns2501_writeb(dvo, NS2501_REG8, ns->reg_8_shadow); - } while (!ok && retries--); + for (i = 0; i < 84; i++) + ns2501_writeb(dvo, ns->regs[i].offset, ns->regs[i].value); } /* set the NS2501 power state */ @@ -439,60 +568,46 @@ static bool ns2501_get_hw_state(struct intel_dvo_device *dvo) if (!ns2501_readb(dvo, NS2501_REG8, &ch)) return false; - if (ch & NS2501_8_PD) - return true; - else - return false; + return ch & NS2501_8_PD; } /* set the NS2501 power state */ static void ns2501_dpms(struct intel_dvo_device *dvo, bool enable) { - bool ok; - int retries = 10; struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv); - unsigned char ch; DRM_DEBUG_KMS("Trying set the dpms of the DVO to %i\n", enable); - ch = ns->reg_8_shadow; + if (enable) { + if (WARN_ON(ns->regs[83].offset != 0x08 || + ns->regs[84].offset != 0x41 || + ns->regs[85].offset != 0xc0)) + return; - if (enable) - ch |= NS2501_8_PD; - else - ch &= ~NS2501_8_PD; - - if (ns->reg_8_set == 0 || ns->reg_8_shadow != ch) { - ns->reg_8_set = 1; - ns->reg_8_shadow = ch; - - do { - ok = true; - ok &= ns2501_writeb(dvo, NS2501_REG8, ch); - ok &= - ns2501_writeb(dvo, 0x34, - enable ? 0x03 : 0x00); - ok &= - ns2501_writeb(dvo, 0x35, - enable ? 0xff : 0x00); - } while (!ok && retries--); - } -} + ns2501_writeb(dvo, 0xc0, ns->regs[85].value | 0x08); -static void ns2501_dump_regs(struct intel_dvo_device *dvo) -{ - uint8_t val; - - ns2501_readb(dvo, NS2501_FREQ_LO, &val); - DRM_DEBUG_KMS("NS2501_FREQ_LO: 0x%02x\n", val); - ns2501_readb(dvo, NS2501_FREQ_HI, &val); - DRM_DEBUG_KMS("NS2501_FREQ_HI: 0x%02x\n", val); - ns2501_readb(dvo, NS2501_REG8, &val); - DRM_DEBUG_KMS("NS2501_REG8: 0x%02x\n", val); - ns2501_readb(dvo, NS2501_REG9, &val); - DRM_DEBUG_KMS("NS2501_REG9: 0x%02x\n", val); - ns2501_readb(dvo, NS2501_REGC, &val); - DRM_DEBUG_KMS("NS2501_REGC: 0x%02x\n", val); + ns2501_writeb(dvo, 0x41, ns->regs[84].value); + + ns2501_writeb(dvo, 0x34, 0x01); + msleep(15); + + ns2501_writeb(dvo, 0x08, 0x35); + if (!(ns->regs[83].value & NS2501_8_BPAS)) + ns2501_writeb(dvo, 0x08, 0x31); + msleep(200); + + ns2501_writeb(dvo, 0x34, 0x03); + + ns2501_writeb(dvo, 0xc0, ns->regs[85].value); + } else { + ns2501_writeb(dvo, 0x34, 0x01); + msleep(200); + + ns2501_writeb(dvo, 0x08, 0x34); + msleep(15); + + ns2501_writeb(dvo, 0x34, 0x00); + } } static void ns2501_destroy(struct intel_dvo_device *dvo) @@ -512,6 +627,5 @@ struct intel_dvo_dev_ops ns2501_ops = { .mode_set = ns2501_mode_set, .dpms = ns2501_dpms, .get_hw_state = ns2501_get_hw_state, - .dump_regs = ns2501_dump_regs, .destroy = ns2501_destroy, }; diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c index 4b7ed52892173ea8fb595d7350f42eec20234591..593b657d3e59e21d3e73dac35086a64a850f8b69 100644 --- a/drivers/gpu/drm/i915/i915_cmd_parser.c +++ b/drivers/gpu/drm/i915/i915_cmd_parser.c @@ -844,8 +844,6 @@ static u32 *vmap_batch(struct drm_i915_gem_object *obj) */ bool i915_needs_cmd_parser(struct intel_engine_cs *ring) { - struct drm_i915_private *dev_priv = ring->dev->dev_private; - if (!ring->needs_cmd_parser) return false; @@ -854,7 +852,7 @@ bool i915_needs_cmd_parser(struct intel_engine_cs *ring) * disabled. That will cause all of the parser's PPGTT checks to * fail. For now, disable parsing when PPGTT is off. */ - if (!dev_priv->mm.aliasing_ppgtt) + if (USES_PPGTT(ring->dev)) return false; return (i915.enable_cmd_parser == 1); diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 9e737b771c40ea2472f3e9f098cb91f363d75c23..063b44817e083649088f59f677fcc300679bd5ec 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -136,7 +136,7 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) obj->last_read_seqno, obj->last_write_seqno, obj->last_fenced_seqno, - i915_cache_level_str(obj->cache_level), + i915_cache_level_str(to_i915(obj->base.dev), obj->cache_level), obj->dirty ? " dirty" : "", obj->madv == I915_MADV_DONTNEED ? " purgeable" : ""); if (obj->base.name) @@ -333,7 +333,7 @@ static int per_file_stats(int id, void *ptr, void *data) } ppgtt = container_of(vma->vm, struct i915_hw_ppgtt, base); - if (ppgtt->ctx && ppgtt->ctx->file_priv != stats->file_priv) + if (ppgtt->file_priv != stats->file_priv) continue; if (obj->ring) /* XXX per-vma statistic */ @@ -515,6 +515,7 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data) { struct drm_info_node *node = m->private; struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; unsigned long flags; struct intel_crtc *crtc; int ret; @@ -534,6 +535,8 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data) seq_printf(m, "No flip due on pipe %c (plane %c)\n", pipe, plane); } else { + u32 addr; + if (atomic_read(&work->pending) < INTEL_FLIP_COMPLETE) { seq_printf(m, "Flip queued on pipe %c (plane %c)\n", pipe, plane); @@ -541,23 +544,35 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data) seq_printf(m, "Flip pending (waiting for vsync) on pipe %c (plane %c)\n", pipe, plane); } + if (work->flip_queued_ring) { + seq_printf(m, "Flip queued on %s at seqno %u, next seqno %u [current breadcrumb %u], completed? %d\n", + work->flip_queued_ring->name, + work->flip_queued_seqno, + dev_priv->next_seqno, + work->flip_queued_ring->get_seqno(work->flip_queued_ring, true), + i915_seqno_passed(work->flip_queued_ring->get_seqno(work->flip_queued_ring, true), + work->flip_queued_seqno)); + } else + seq_printf(m, "Flip not associated with any ring\n"); + seq_printf(m, "Flip queued on frame %d, (was ready on frame %d), now %d\n", + work->flip_queued_vblank, + work->flip_ready_vblank, + drm_vblank_count(dev, crtc->pipe)); if (work->enable_stall_check) seq_puts(m, "Stall check enabled, "); else seq_puts(m, "Stall check waiting for page flip ioctl, "); seq_printf(m, "%d prepares\n", atomic_read(&work->pending)); - if (work->old_fb_obj) { - struct drm_i915_gem_object *obj = work->old_fb_obj; - if (obj) - seq_printf(m, "Old framebuffer gtt_offset 0x%08lx\n", - i915_gem_obj_ggtt_offset(obj)); - } + if (INTEL_INFO(dev)->gen >= 4) + addr = I915_HI_DISPBASE(I915_READ(DSPSURF(crtc->plane))); + else + addr = I915_READ(DSPADDR(crtc->plane)); + seq_printf(m, "Current scanout address 0x%08x\n", addr); + if (work->pending_flip_obj) { - struct drm_i915_gem_object *obj = work->pending_flip_obj; - if (obj) - seq_printf(m, "New framebuffer gtt_offset 0x%08lx\n", - i915_gem_obj_ggtt_offset(obj)); + seq_printf(m, "New framebuffer address 0x%08lx\n", (long)work->gtt_offset); + seq_printf(m, "MMIO update completed? %d\n", addr == work->gtt_offset); } } spin_unlock_irqrestore(&dev->event_lock, flags); @@ -650,7 +665,6 @@ static int i915_interrupt_info(struct seq_file *m, void *data) intel_runtime_pm_get(dev_priv); if (IS_CHERRYVIEW(dev)) { - int i; seq_printf(m, "Master Interrupt Control:\t%08x\n", I915_READ(GEN8_MASTER_IRQ)); @@ -662,7 +676,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data) I915_READ(VLV_IIR_RW)); seq_printf(m, "Display IMR:\t%08x\n", I915_READ(VLV_IMR)); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) seq_printf(m, "Pipe %c stat:\t%08x\n", pipe_name(pipe), I915_READ(PIPESTAT(pipe))); @@ -702,7 +716,13 @@ static int i915_interrupt_info(struct seq_file *m, void *data) i, I915_READ(GEN8_GT_IER(i))); } - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { + if (!intel_display_power_enabled(dev_priv, + POWER_DOMAIN_PIPE(pipe))) { + seq_printf(m, "Pipe %c power disabled\n", + pipe_name(pipe)); + continue; + } seq_printf(m, "Pipe %c IMR:\t%08x\n", pipe_name(pipe), I915_READ(GEN8_DE_PIPE_IMR(pipe))); @@ -743,7 +763,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data) I915_READ(VLV_IIR_RW)); seq_printf(m, "Display IMR:\t%08x\n", I915_READ(VLV_IMR)); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) seq_printf(m, "Pipe %c stat:\t%08x\n", pipe_name(pipe), I915_READ(PIPESTAT(pipe))); @@ -779,7 +799,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data) I915_READ(IIR)); seq_printf(m, "Interrupt mask: %08x\n", I915_READ(IMR)); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) seq_printf(m, "Pipe %c stat: %08x\n", pipe_name(pipe), I915_READ(PIPESTAT(pipe))); @@ -927,7 +947,7 @@ static ssize_t i915_error_state_read(struct file *file, char __user *userbuf, ssize_t ret_count = 0; int ret; - ret = i915_error_state_buf_init(&error_str, count, *pos); + ret = i915_error_state_buf_init(&error_str, to_i915(error_priv->dev), count, *pos); if (ret) return ret; @@ -1024,6 +1044,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused) u32 rpstat, cagf, reqf; u32 rpupei, rpcurup, rpprevup; u32 rpdownei, rpcurdown, rpprevdown; + u32 pm_ier, pm_imr, pm_isr, pm_iir, pm_mask; int max_freq; /* RPSTAT1 is in the GT power well */ @@ -1061,12 +1082,21 @@ static int i915_frequency_info(struct seq_file *m, void *unused) gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL); mutex_unlock(&dev->struct_mutex); + if (IS_GEN6(dev) || IS_GEN7(dev)) { + pm_ier = I915_READ(GEN6_PMIER); + pm_imr = I915_READ(GEN6_PMIMR); + pm_isr = I915_READ(GEN6_PMISR); + pm_iir = I915_READ(GEN6_PMIIR); + pm_mask = I915_READ(GEN6_PMINTRMSK); + } else { + pm_ier = I915_READ(GEN8_GT_IER(2)); + pm_imr = I915_READ(GEN8_GT_IMR(2)); + pm_isr = I915_READ(GEN8_GT_ISR(2)); + pm_iir = I915_READ(GEN8_GT_IIR(2)); + pm_mask = I915_READ(GEN6_PMINTRMSK); + } seq_printf(m, "PM IER=0x%08x IMR=0x%08x ISR=0x%08x IIR=0x%08x, MASK=0x%08x\n", - I915_READ(GEN6_PMIER), - I915_READ(GEN6_PMIMR), - I915_READ(GEN6_PMISR), - I915_READ(GEN6_PMIIR), - I915_READ(GEN6_PMINTRMSK)); + pm_ier, pm_imr, pm_isr, pm_iir, pm_mask); seq_printf(m, "GT_PERF_STATUS: 0x%08x\n", gt_perf_status); seq_printf(m, "Render p-state ratio: %d\n", (gt_perf_status & 0xff00) >> 8); @@ -1365,7 +1395,7 @@ static int i915_drpc_info(struct seq_file *m, void *unused) if (IS_VALLEYVIEW(dev)) return vlv_drpc_info(m); - else if (IS_GEN6(dev) || IS_GEN7(dev)) + else if (INTEL_INFO(dev)->gen >= 6) return gen6_drpc_info(m); else return ironlake_drpc_info(m); @@ -1433,6 +1463,47 @@ static int i915_fbc_status(struct seq_file *m, void *unused) return 0; } +static int i915_fbc_fc_get(void *data, u64 *val) +{ + struct drm_device *dev = data; + struct drm_i915_private *dev_priv = dev->dev_private; + + if (INTEL_INFO(dev)->gen < 7 || !HAS_FBC(dev)) + return -ENODEV; + + drm_modeset_lock_all(dev); + *val = dev_priv->fbc.false_color; + drm_modeset_unlock_all(dev); + + return 0; +} + +static int i915_fbc_fc_set(void *data, u64 val) +{ + struct drm_device *dev = data; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 reg; + + if (INTEL_INFO(dev)->gen < 7 || !HAS_FBC(dev)) + return -ENODEV; + + drm_modeset_lock_all(dev); + + reg = I915_READ(ILK_DPFC_CONTROL); + dev_priv->fbc.false_color = val; + + I915_WRITE(ILK_DPFC_CONTROL, val ? + (reg | FBC_CTL_FALSE_COLOR) : + (reg & ~FBC_CTL_FALSE_COLOR)); + + drm_modeset_unlock_all(dev); + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(i915_fbc_fc_fops, + i915_fbc_fc_get, i915_fbc_fc_set, + "%llu\n"); + static int i915_ips_status(struct seq_file *m, void *unused) { struct drm_info_node *node = m->private; @@ -1630,6 +1701,14 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) return 0; } +static void describe_ctx_ringbuf(struct seq_file *m, + struct intel_ringbuffer *ringbuf) +{ + seq_printf(m, " (ringbuffer, space: %d, head: %u, tail: %u, last head: %d)", + ringbuf->space, ringbuf->head, ringbuf->tail, + ringbuf->last_retired_head); +} + static int i915_context_status(struct seq_file *m, void *unused) { struct drm_info_node *node = m->private; @@ -1656,16 +1735,168 @@ static int i915_context_status(struct seq_file *m, void *unused) } list_for_each_entry(ctx, &dev_priv->context_list, link) { - if (ctx->legacy_hw_ctx.rcs_state == NULL) + if (!i915.enable_execlists && + ctx->legacy_hw_ctx.rcs_state == NULL) continue; seq_puts(m, "HW context "); describe_ctx(m, ctx); - for_each_ring(ring, dev_priv, i) + for_each_ring(ring, dev_priv, i) { + if (ring->default_context == ctx) + seq_printf(m, "(default context %s) ", + ring->name); + } + + if (i915.enable_execlists) { + seq_putc(m, '\n'); + for_each_ring(ring, dev_priv, i) { + struct drm_i915_gem_object *ctx_obj = + ctx->engine[i].state; + struct intel_ringbuffer *ringbuf = + ctx->engine[i].ringbuf; + + seq_printf(m, "%s: ", ring->name); + if (ctx_obj) + describe_obj(m, ctx_obj); + if (ringbuf) + describe_ctx_ringbuf(m, ringbuf); + seq_putc(m, '\n'); + } + } else { + describe_obj(m, ctx->legacy_hw_ctx.rcs_state); + } + + seq_putc(m, '\n'); + } + + mutex_unlock(&dev->struct_mutex); + + return 0; +} + +static int i915_dump_lrc(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_engine_cs *ring; + struct intel_context *ctx; + int ret, i; + + if (!i915.enable_execlists) { + seq_printf(m, "Logical Ring Contexts are disabled\n"); + return 0; + } + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + list_for_each_entry(ctx, &dev_priv->context_list, link) { + for_each_ring(ring, dev_priv, i) { + struct drm_i915_gem_object *ctx_obj = ctx->engine[i].state; + if (ring->default_context == ctx) - seq_printf(m, "(default context %s) ", ring->name); + continue; + + if (ctx_obj) { + struct page *page = i915_gem_object_get_page(ctx_obj, 1); + uint32_t *reg_state = kmap_atomic(page); + int j; + + seq_printf(m, "CONTEXT: %s %u\n", ring->name, + intel_execlists_ctx_id(ctx_obj)); + + for (j = 0; j < 0x600 / sizeof(u32) / 4; j += 4) { + seq_printf(m, "\t[0x%08lx] 0x%08x 0x%08x 0x%08x 0x%08x\n", + i915_gem_obj_ggtt_offset(ctx_obj) + 4096 + (j * 4), + reg_state[j], reg_state[j + 1], + reg_state[j + 2], reg_state[j + 3]); + } + kunmap_atomic(reg_state); + + seq_putc(m, '\n'); + } + } + } + + mutex_unlock(&dev->struct_mutex); + + return 0; +} + +static int i915_execlists(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_engine_cs *ring; + u32 status_pointer; + u8 read_pointer; + u8 write_pointer; + u32 status; + u32 ctx_id; + struct list_head *cursor; + int ring_id, i; + int ret; + + if (!i915.enable_execlists) { + seq_puts(m, "Logical Ring Contexts are disabled\n"); + return 0; + } + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + for_each_ring(ring, dev_priv, ring_id) { + struct intel_ctx_submit_request *head_req = NULL; + int count = 0; + unsigned long flags; + + seq_printf(m, "%s\n", ring->name); + + status = I915_READ(RING_EXECLIST_STATUS(ring)); + ctx_id = I915_READ(RING_EXECLIST_STATUS(ring) + 4); + seq_printf(m, "\tExeclist status: 0x%08X, context: %u\n", + status, ctx_id); + + status_pointer = I915_READ(RING_CONTEXT_STATUS_PTR(ring)); + seq_printf(m, "\tStatus pointer: 0x%08X\n", status_pointer); + + read_pointer = ring->next_context_status_buffer; + write_pointer = status_pointer & 0x07; + if (read_pointer > write_pointer) + write_pointer += 6; + seq_printf(m, "\tRead pointer: 0x%08X, write pointer 0x%08X\n", + read_pointer, write_pointer); + + for (i = 0; i < 6; i++) { + status = I915_READ(RING_CONTEXT_STATUS_BUF(ring) + 8*i); + ctx_id = I915_READ(RING_CONTEXT_STATUS_BUF(ring) + 8*i + 4); + + seq_printf(m, "\tStatus buffer %d: 0x%08X, context: %u\n", + i, status, ctx_id); + } + + spin_lock_irqsave(&ring->execlist_lock, flags); + list_for_each(cursor, &ring->execlist_queue) + count++; + head_req = list_first_entry_or_null(&ring->execlist_queue, + struct intel_ctx_submit_request, execlist_link); + spin_unlock_irqrestore(&ring->execlist_lock, flags); + + seq_printf(m, "\t%d requests in queue\n", count); + if (head_req) { + struct drm_i915_gem_object *ctx_obj; + + ctx_obj = head_req->ctx->engine[ring_id].state; + seq_printf(m, "\tHead request id: %u\n", + intel_execlists_ctx_id(ctx_obj)); + seq_printf(m, "\tHead request tail: %u\n", + head_req->tail); + } - describe_obj(m, ctx->legacy_hw_ctx.rcs_state); seq_putc(m, '\n'); } @@ -1774,7 +2005,13 @@ static int per_file_ctx(int id, void *ptr, void *data) { struct intel_context *ctx = ptr; struct seq_file *m = data; - struct i915_hw_ppgtt *ppgtt = ctx_to_ppgtt(ctx); + struct i915_hw_ppgtt *ppgtt = ctx->ppgtt; + + if (!ppgtt) { + seq_printf(m, " no ppgtt for context %d\n", + ctx->user_handle); + return 0; + } if (i915_gem_context_is_default(ctx)) seq_puts(m, " default context:\n"); @@ -1834,8 +2071,7 @@ static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev) seq_printf(m, "pd gtt offset: 0x%08x\n", ppgtt->pd_offset); ppgtt->debug_dump(ppgtt, m); - } else - return; + } list_for_each_entry_reverse(file, &dev->filelist, lhead) { struct drm_i915_file_private *file_priv = file->driver_priv; @@ -2406,6 +2642,40 @@ static int i915_shared_dplls_info(struct seq_file *m, void *unused) return 0; } +static int i915_wa_registers(struct seq_file *m, void *unused) +{ + int i; + int ret; + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + intel_runtime_pm_get(dev_priv); + + seq_printf(m, "Workarounds applied: %d\n", dev_priv->num_wa_regs); + for (i = 0; i < dev_priv->num_wa_regs; ++i) { + u32 addr, mask; + + addr = dev_priv->intel_wa_regs[i].addr; + mask = dev_priv->intel_wa_regs[i].mask; + dev_priv->intel_wa_regs[i].value = I915_READ(addr) | mask; + if (dev_priv->intel_wa_regs[i].addr) + seq_printf(m, "0x%X: 0x%08X, mask: 0x%08X\n", + dev_priv->intel_wa_regs[i].addr, + dev_priv->intel_wa_regs[i].value, + dev_priv->intel_wa_regs[i].mask); + } + + intel_runtime_pm_put(dev_priv); + mutex_unlock(&dev->struct_mutex); + + return 0; +} + struct pipe_crc_info { const char *name; struct drm_device *dev; @@ -2667,8 +2937,7 @@ static int i9xx_pipe_crc_auto_source(struct drm_device *dev, enum pipe pipe, *source = INTEL_PIPE_CRC_SOURCE_PIPE; drm_modeset_lock_all(dev); - list_for_each_entry(encoder, &dev->mode_config.encoder_list, - base.head) { + for_each_intel_encoder(dev, encoder) { if (!encoder->base.crtc) continue; @@ -3557,9 +3826,6 @@ i915_drop_caches_set(void *data, u64 val) { struct drm_device *dev = data; struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_i915_gem_object *obj, *next; - struct i915_address_space *vm; - struct i915_vma *vma, *x; int ret; DRM_DEBUG("Dropping caches: 0x%08llx\n", val); @@ -3579,29 +3845,11 @@ i915_drop_caches_set(void *data, u64 val) if (val & (DROP_RETIRE | DROP_ACTIVE)) i915_gem_retire_requests(dev); - if (val & DROP_BOUND) { - list_for_each_entry(vm, &dev_priv->vm_list, global_link) { - list_for_each_entry_safe(vma, x, &vm->inactive_list, - mm_list) { - if (vma->pin_count) - continue; + if (val & DROP_BOUND) + i915_gem_shrink(dev_priv, LONG_MAX, I915_SHRINK_BOUND); - ret = i915_vma_unbind(vma); - if (ret) - goto unlock; - } - } - } - - if (val & DROP_UNBOUND) { - list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list, - global_list) - if (obj->pages_pin_count == 0) { - ret = i915_gem_object_put_pages(obj); - if (ret) - goto unlock; - } - } + if (val & DROP_UNBOUND) + i915_gem_shrink(dev_priv, LONG_MAX, I915_SHRINK_UNBOUND); unlock: mutex_unlock(&dev->struct_mutex); @@ -3923,6 +4171,8 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_opregion", i915_opregion, 0}, {"i915_gem_framebuffer", i915_gem_framebuffer_info, 0}, {"i915_context_status", i915_context_status, 0}, + {"i915_dump_lrc", i915_dump_lrc, 0}, + {"i915_execlists", i915_execlists, 0}, {"i915_gen6_forcewake_count", i915_gen6_forcewake_count_info, 0}, {"i915_swizzle_info", i915_swizzle_info, 0}, {"i915_ppgtt_info", i915_ppgtt_info, 0}, @@ -3936,6 +4186,7 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_semaphore_status", i915_semaphore_status, 0}, {"i915_shared_dplls_info", i915_shared_dplls_info, 0}, {"i915_dp_mst_info", i915_dp_mst_info, 0}, + {"i915_wa_registers", i915_wa_registers, 0}, }; #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list) @@ -3957,6 +4208,7 @@ static const struct i915_debugfs_files { {"i915_pri_wm_latency", &i915_pri_wm_latency_fops}, {"i915_spr_wm_latency", &i915_spr_wm_latency_fops}, {"i915_cur_wm_latency", &i915_cur_wm_latency_fops}, + {"i915_fbc_false_color", &i915_fbc_fc_fops}, }; void intel_display_crc_init(struct drm_device *dev) @@ -3964,7 +4216,7 @@ void intel_display_crc_init(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; enum pipe pipe; - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe]; pipe_crc->opened = false; diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 9933c26017edae26a2bab5879e8be0543735c027..1403b01e821695cce6c42cb8b3704eb284aba17e 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -28,9 +28,11 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include +#include #include "intel_drv.h" #include #include "i915_drv.h" @@ -196,7 +198,7 @@ static int i915_initialize(struct drm_device *dev, drm_i915_init_t *init) struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; int ret; - master_priv->sarea = drm_getsarea(dev); + master_priv->sarea = drm_legacy_getsarea(dev); if (master_priv->sarea) { master_priv->sarea_priv = (drm_i915_sarea_t *) ((u8 *)master_priv->sarea->handle + init->sarea_priv_offset); @@ -999,7 +1001,7 @@ static int i915_getparam(struct drm_device *dev, void *data, value = HAS_WT(dev); break; case I915_PARAM_HAS_ALIASING_PPGTT: - value = dev_priv->mm.aliasing_ppgtt || USES_FULL_PPGTT(dev); + value = USES_PPGTT(dev); break; case I915_PARAM_HAS_WAIT_TIMEOUT: value = 1; @@ -1355,8 +1357,6 @@ static int i915_load_modeset_init(struct drm_device *dev) if (ret) goto cleanup_irq; - INIT_WORK(&dev_priv->console_resume_work, intel_console_resume); - intel_modeset_gem_init(dev); /* Always safe in the mode setting case. */ @@ -1382,7 +1382,7 @@ static int i915_load_modeset_init(struct drm_device *dev) * scanning against hotplug events. Hence do this first and ignore the * tiny window where we will loose hotplug notifactions. */ - intel_fbdev_initial_config(dev); + async_schedule(intel_fbdev_initial_config, dev_priv); drm_kms_helper_poll_init(dev); @@ -1393,7 +1393,6 @@ static int i915_load_modeset_init(struct drm_device *dev) i915_gem_cleanup_ringbuffer(dev); i915_gem_context_fini(dev); mutex_unlock(&dev->struct_mutex); - WARN_ON(dev_priv->mm.aliasing_ppgtt); cleanup_irq: drm_irq_uninstall(dev); cleanup_gem_stolen: @@ -1536,10 +1535,10 @@ static void intel_device_info_runtime_init(struct drm_device *dev) info = (struct intel_device_info *)&dev_priv->info; if (IS_VALLEYVIEW(dev)) - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) info->num_sprites[pipe] = 2; else - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) info->num_sprites[pipe] = 1; if (i915.disable_display) { @@ -1608,9 +1607,10 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) dev->dev_private = dev_priv; dev_priv->dev = dev; - /* copy initial configuration to dev_priv->info */ + /* Setup the write-once "constant" device info */ device_info = (struct intel_device_info *)&dev_priv->info; - *device_info = *info; + memcpy(device_info, info, sizeof(dev_priv->info)); + device_info->device_id = dev->pdev->device; spin_lock_init(&dev_priv->irq_lock); spin_lock_init(&dev_priv->gpu_error.lock); @@ -1822,7 +1822,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) arch_phys_wc_del(dev_priv->gtt.mtrr); io_mapping_free(dev_priv->gtt.mappable); out_gtt: - dev_priv->gtt.base.cleanup(&dev_priv->gtt.base); + i915_global_gtt_cleanup(dev); out_regs: intel_uncore_fini(dev); pci_iounmap(dev->pdev, dev_priv->regs); @@ -1869,7 +1869,6 @@ int i915_driver_unload(struct drm_device *dev) if (drm_core_check_feature(dev, DRIVER_MODESET)) { intel_fbdev_fini(dev); intel_modeset_cleanup(dev); - cancel_work_sync(&dev_priv->console_resume_work); /* * free the memory space allocated for the child device @@ -1902,7 +1901,6 @@ int i915_driver_unload(struct drm_device *dev) mutex_lock(&dev->struct_mutex); i915_gem_cleanup_ringbuffer(dev); i915_gem_context_fini(dev); - WARN_ON(dev_priv->mm.aliasing_ppgtt); mutex_unlock(&dev->struct_mutex); i915_gem_cleanup_stolen(dev); @@ -1910,8 +1908,6 @@ int i915_driver_unload(struct drm_device *dev) i915_free_hws(dev); } - WARN_ON(!list_empty(&dev_priv->vm_list)); - drm_vblank_cleanup(dev); intel_teardown_gmbus(dev); @@ -1921,7 +1917,7 @@ int i915_driver_unload(struct drm_device *dev) destroy_workqueue(dev_priv->wq); pm_qos_remove_request(&dev_priv->pm_qos); - dev_priv->gtt.base.cleanup(&dev_priv->gtt.base); + i915_global_gtt_cleanup(dev); intel_uncore_fini(dev); if (dev_priv->regs != NULL) @@ -1986,6 +1982,9 @@ void i915_driver_preclose(struct drm_device *dev, struct drm_file *file) i915_gem_context_close(dev, file); i915_gem_release(dev, file); mutex_unlock(&dev->struct_mutex); + + if (drm_core_check_feature(dev, DRIVER_MODESET)) + intel_modeset_preclose(dev, file); } void i915_driver_postclose(struct drm_device *dev, struct drm_file *file) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index e27cdbe9d524a79dbd4168febc15de0660e82d9f..055d5e7fbf12a14473c7b82f314630d1e5615b9c 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -481,6 +481,10 @@ bool i915_semaphore_is_enabled(struct drm_device *dev) if (i915.semaphores >= 0) return i915.semaphores; + /* TODO: make semaphores and Execlists play nicely together */ + if (i915.enable_execlists) + return false; + /* Until we get further testing... */ if (IS_GEN8(dev)) return false; @@ -524,6 +528,10 @@ static void intel_suspend_encoders(struct drm_i915_private *dev_priv) drm_modeset_unlock_all(dev); } +static int intel_suspend_complete(struct drm_i915_private *dev_priv); +static int intel_resume_prepare(struct drm_i915_private *dev_priv, + bool rpm_resume); + static int i915_drm_freeze(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -591,9 +599,7 @@ static int i915_drm_freeze(struct drm_device *dev) intel_uncore_forcewake_reset(dev, false); intel_opregion_fini(dev); - console_lock(); - intel_fbdev_set_suspend(dev, FBINFO_STATE_SUSPENDED); - console_unlock(); + intel_fbdev_set_suspend(dev, FBINFO_STATE_SUSPENDED, true); dev_priv->suspend_count++; @@ -632,30 +638,20 @@ int i915_suspend(struct drm_device *dev, pm_message_t state) return 0; } -void intel_console_resume(struct work_struct *work) -{ - struct drm_i915_private *dev_priv = - container_of(work, struct drm_i915_private, - console_resume_work); - struct drm_device *dev = dev_priv->dev; - - console_lock(); - intel_fbdev_set_suspend(dev, FBINFO_STATE_RUNNING); - console_unlock(); -} - static int i915_drm_thaw_early(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + int ret; - if (IS_HASWELL(dev) || IS_BROADWELL(dev)) - hsw_disable_pc8(dev_priv); + ret = intel_resume_prepare(dev_priv, false); + if (ret) + DRM_ERROR("Resume prepare failed: %d,Continuing resume\n", ret); intel_uncore_early_sanitize(dev, true); intel_uncore_sanitize(dev); intel_power_domains_init_hw(dev_priv); - return 0; + return ret; } static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings) @@ -714,17 +710,7 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings) intel_opregion_init(dev); - /* - * The console lock can be pretty contented on resume due - * to all the printk activity. Try to keep it out of the hot - * path of resume if possible. - */ - if (console_trylock()) { - intel_fbdev_set_suspend(dev, FBINFO_STATE_RUNNING); - console_unlock(); - } else { - schedule_work(&dev_priv->console_resume_work); - } + intel_fbdev_set_suspend(dev, FBINFO_STATE_RUNNING, false); mutex_lock(&dev_priv->modeset_restore_lock); dev_priv->modeset_restore = MODESET_DONE; @@ -858,7 +844,13 @@ int i915_reset(struct drm_device *dev) !dev_priv->ums.mm_suspended) { dev_priv->ums.mm_suspended = 0; + /* Used to prevent gem_check_wedged returning -EAGAIN during gpu reset */ + dev_priv->gpu_error.reload_in_reset = true; + ret = i915_gem_init_hw(dev); + + dev_priv->gpu_error.reload_in_reset = false; + mutex_unlock(&dev->struct_mutex); if (ret) { DRM_ERROR("Failed hw init on reset %d\n", ret); @@ -879,8 +871,6 @@ int i915_reset(struct drm_device *dev) */ if (INTEL_INFO(dev)->gen > 5) intel_reset_gt_powersave(dev); - - intel_hpd_init(dev); } else { mutex_unlock(&dev->struct_mutex); } @@ -941,6 +931,7 @@ static int i915_pm_suspend_late(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); struct drm_device *drm_dev = pci_get_drvdata(pdev); struct drm_i915_private *dev_priv = drm_dev->dev_private; + int ret; /* * We have a suspedn ordering issue with the snd-hda driver also @@ -954,13 +945,16 @@ static int i915_pm_suspend_late(struct device *dev) if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; - if (IS_HASWELL(drm_dev) || IS_BROADWELL(drm_dev)) - hsw_enable_pc8(dev_priv); + ret = intel_suspend_complete(dev_priv); - pci_disable_device(pdev); - pci_set_power_state(pdev, PCI_D3hot); + if (ret) + DRM_ERROR("Suspend complete failed: %d\n", ret); + else { + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + } - return 0; + return ret; } static int i915_pm_resume_early(struct device *dev) @@ -1016,23 +1010,26 @@ static int i915_pm_poweroff(struct device *dev) return i915_drm_freeze(drm_dev); } -static int hsw_runtime_suspend(struct drm_i915_private *dev_priv) +static int hsw_suspend_complete(struct drm_i915_private *dev_priv) { hsw_enable_pc8(dev_priv); return 0; } -static int snb_runtime_resume(struct drm_i915_private *dev_priv) +static int snb_resume_prepare(struct drm_i915_private *dev_priv, + bool rpm_resume) { struct drm_device *dev = dev_priv->dev; - intel_init_pch_refclk(dev); + if (rpm_resume) + intel_init_pch_refclk(dev); return 0; } -static int hsw_runtime_resume(struct drm_i915_private *dev_priv) +static int hsw_resume_prepare(struct drm_i915_private *dev_priv, + bool rpm_resume) { hsw_disable_pc8(dev_priv); @@ -1328,7 +1325,7 @@ static void vlv_check_no_gt_access(struct drm_i915_private *dev_priv) I915_WRITE(VLV_GTLC_PW_STATUS, VLV_GTLC_ALLOWWAKEERR); } -static int vlv_runtime_suspend(struct drm_i915_private *dev_priv) +static int vlv_suspend_complete(struct drm_i915_private *dev_priv) { u32 mask; int err; @@ -1368,7 +1365,8 @@ static int vlv_runtime_suspend(struct drm_i915_private *dev_priv) return err; } -static int vlv_runtime_resume(struct drm_i915_private *dev_priv) +static int vlv_resume_prepare(struct drm_i915_private *dev_priv, + bool rpm_resume) { struct drm_device *dev = dev_priv->dev; int err; @@ -1393,8 +1391,10 @@ static int vlv_runtime_resume(struct drm_i915_private *dev_priv) vlv_check_no_gt_access(dev_priv); - intel_init_clock_gating(dev); - i915_gem_restore_fences(dev); + if (rpm_resume) { + intel_init_clock_gating(dev); + i915_gem_restore_fences(dev); + } return ret; } @@ -1409,7 +1409,9 @@ static int intel_runtime_suspend(struct device *device) if (WARN_ON_ONCE(!(dev_priv->rps.enabled && intel_enable_rc6(dev)))) return -ENODEV; - WARN_ON(!HAS_RUNTIME_PM(dev)); + if (WARN_ON_ONCE(!HAS_RUNTIME_PM(dev))) + return -ENODEV; + assert_force_wake_inactive(dev_priv); DRM_DEBUG_KMS("Suspending device\n"); @@ -1446,17 +1448,7 @@ static int intel_runtime_suspend(struct device *device) cancel_work_sync(&dev_priv->rps.work); intel_runtime_pm_disable_interrupts(dev); - if (IS_GEN6(dev)) { - ret = 0; - } else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { - ret = hsw_runtime_suspend(dev_priv); - } else if (IS_VALLEYVIEW(dev)) { - ret = vlv_runtime_suspend(dev_priv); - } else { - ret = -ENODEV; - WARN_ON(1); - } - + ret = intel_suspend_complete(dev_priv); if (ret) { DRM_ERROR("Runtime suspend failed, disabling it (%d)\n", ret); intel_runtime_pm_restore_interrupts(dev); @@ -1468,13 +1460,29 @@ static int intel_runtime_suspend(struct device *device) dev_priv->pm.suspended = true; /* - * current versions of firmware which depend on this opregion - * notification have repurposed the D1 definition to mean - * "runtime suspended" vs. what you would normally expect (D3) - * to distinguish it from notifications that might be sent - * via the suspend path. + * FIXME: We really should find a document that references the arguments + * used below! */ - intel_opregion_notify_adapter(dev, PCI_D1); + if (IS_HASWELL(dev)) { + /* + * current versions of firmware which depend on this opregion + * notification have repurposed the D1 definition to mean + * "runtime suspended" vs. what you would normally expect (D3) + * to distinguish it from notifications that might be sent via + * the suspend path. + */ + intel_opregion_notify_adapter(dev, PCI_D1); + } else { + /* + * On Broadwell, if we use PCI_D1 the PCH DDI ports will stop + * being detected, and the call we do at intel_runtime_resume() + * won't be able to restore them. Since PCI_D3hot matches the + * actual specification and appears to be working, use it. Let's + * assume the other non-Haswell platforms will stay the same as + * Broadwell. + */ + intel_opregion_notify_adapter(dev, PCI_D3hot); + } DRM_DEBUG_KMS("Device suspended\n"); return 0; @@ -1487,24 +1495,15 @@ static int intel_runtime_resume(struct device *device) struct drm_i915_private *dev_priv = dev->dev_private; int ret; - WARN_ON(!HAS_RUNTIME_PM(dev)); + if (WARN_ON_ONCE(!HAS_RUNTIME_PM(dev))) + return -ENODEV; DRM_DEBUG_KMS("Resuming device\n"); intel_opregion_notify_adapter(dev, PCI_D0); dev_priv->pm.suspended = false; - if (IS_GEN6(dev)) { - ret = snb_runtime_resume(dev_priv); - } else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { - ret = hsw_runtime_resume(dev_priv); - } else if (IS_VALLEYVIEW(dev)) { - ret = vlv_runtime_resume(dev_priv); - } else { - WARN_ON(1); - ret = -ENODEV; - } - + ret = intel_resume_prepare(dev_priv, true); /* * No point of rolling back things in case of an error, as the best * we can do is to hope that things will still work (and disable RPM). @@ -1523,6 +1522,48 @@ static int intel_runtime_resume(struct device *device) return ret; } +/* + * This function implements common functionality of runtime and system + * suspend sequence. + */ +static int intel_suspend_complete(struct drm_i915_private *dev_priv) +{ + struct drm_device *dev = dev_priv->dev; + int ret; + + if (IS_HASWELL(dev) || IS_BROADWELL(dev)) + ret = hsw_suspend_complete(dev_priv); + else if (IS_VALLEYVIEW(dev)) + ret = vlv_suspend_complete(dev_priv); + else + ret = 0; + + return ret; +} + +/* + * This function implements common functionality of runtime and system + * resume sequence. Variable rpm_resume used for implementing different + * code paths. + */ +static int intel_resume_prepare(struct drm_i915_private *dev_priv, + bool rpm_resume) +{ + struct drm_device *dev = dev_priv->dev; + int ret; + + if (IS_GEN6(dev)) + ret = snb_resume_prepare(dev_priv, rpm_resume); + else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) + ret = hsw_resume_prepare(dev_priv, rpm_resume); + else if (IS_VALLEYVIEW(dev)) + ret = vlv_resume_prepare(dev_priv, rpm_resume); + else + ret = 0; + + return ret; +} + static const struct dev_pm_ops i915_pm_ops = { .suspend = i915_pm_suspend, .suspend_late = i915_pm_suspend_late, @@ -1572,6 +1613,7 @@ static struct drm_driver driver = { .lastclose = i915_driver_lastclose, .preclose = i915_driver_preclose, .postclose = i915_driver_postclose, + .set_busid = drm_pci_set_busid, /* Used in place of i915_pm_ops for non-DRIVER_MODESET */ .suspend = i915_suspend, @@ -1663,6 +1705,8 @@ static void __exit i915_exit(void) module_init(i915_init); module_exit(i915_exit); -MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_AUTHOR("Tungsten Graphics, Inc."); +MODULE_AUTHOR("Intel Corporation"); + MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3524306d8cfbf7b9d7e93b2bd076774eb69b36bc..16a6f6d187a193ca8cee8f9cae444a75aa7c4533 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -35,11 +35,15 @@ #include "i915_reg.h" #include "intel_bios.h" #include "intel_ringbuffer.h" +#include "intel_lrc.h" #include "i915_gem_gtt.h" +#include "i915_gem_render_state.h" #include #include #include #include +#include /* for struct drm_dma_handle */ +#include #include #include #include @@ -49,11 +53,9 @@ /* General customization: */ -#define DRIVER_AUTHOR "Tungsten Graphics, Inc." - #define DRIVER_NAME "i915" #define DRIVER_DESC "Intel Graphics" -#define DRIVER_DATE "20140725" +#define DRIVER_DATE "20140905" enum pipe { INVALID_PIPE = -1, @@ -162,7 +164,10 @@ enum hpd_pin { I915_GEM_DOMAIN_INSTRUCTION | \ I915_GEM_DOMAIN_VERTEX) -#define for_each_pipe(p) for ((p) = 0; (p) < INTEL_INFO(dev)->num_pipes; (p)++) +#define for_each_pipe(__dev_priv, __p) \ + for ((__p) = 0; (__p) < INTEL_INFO(__dev_priv)->num_pipes; (__p)++) +#define for_each_plane(pipe, p) \ + for ((p) = 0; (p) < INTEL_INFO(dev)->num_sprites[(pipe)] + 1; (p)++) #define for_each_sprite(p, s) for ((s) = 0; (s) < INTEL_INFO(dev)->num_sprites[(p)]; (s)++) #define for_each_crtc(dev, crtc) \ @@ -171,6 +176,11 @@ enum hpd_pin { #define for_each_intel_crtc(dev, intel_crtc) \ list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) +#define for_each_intel_encoder(dev, intel_encoder) \ + list_for_each_entry(intel_encoder, \ + &(dev)->mode_config.encoder_list, \ + base.head) + #define for_each_encoder_on_crtc(dev, __crtc, intel_encoder) \ list_for_each_entry((intel_encoder), &(dev)->mode_config.encoder_list, base.head) \ if ((intel_encoder)->base.crtc == (__crtc)) @@ -198,10 +208,13 @@ enum intel_dpll_id { #define I915_NUM_PLLS 2 struct intel_dpll_hw_state { + /* i9xx, pch plls */ uint32_t dpll; uint32_t dpll_md; uint32_t fp0; uint32_t fp1; + + /* hsw, bdw */ uint32_t wrpll; }; @@ -277,8 +290,10 @@ struct intel_opregion { struct intel_overlay; struct intel_overlay_error_state; +struct drm_local_map; + struct drm_i915_master_private { - drm_local_map_t *sarea; + struct drm_local_map *sarea; struct _drm_i915_sarea *sarea_priv; }; #define I915_FENCE_REG_NONE -1 @@ -388,6 +403,7 @@ struct drm_i915_error_state { pid_t pid; char comm[TASK_COMM_LEN]; } ring[I915_NUM_RINGS]; + struct drm_i915_error_buffer { u32 size; u32 name; @@ -406,6 +422,7 @@ struct drm_i915_error_state { } **active_bo, **pinned_bo; u32 *active_bo_count, *pinned_bo_count; + u32 vm_count; }; struct intel_connector; @@ -551,6 +568,7 @@ struct intel_uncore { struct intel_device_info { u32 display_mmio_offset; + u16 device_id; u8 num_pipes:3; u8 num_sprites[I915_MAX_PIPES]; u8 gen; @@ -615,13 +633,21 @@ struct intel_context { uint8_t remap_slice; struct drm_i915_file_private *file_priv; struct i915_ctx_hang_stats hang_stats; - struct i915_address_space *vm; + struct i915_hw_ppgtt *ppgtt; + /* Legacy ring buffer submission */ struct { struct drm_i915_gem_object *rcs_state; bool initialized; } legacy_hw_ctx; + /* Execlists */ + bool rcs_initialized; + struct { + struct drm_i915_gem_object *state; + struct intel_ringbuffer *ringbuf; + } engine[I915_NUM_RINGS]; + struct list_head link; }; @@ -635,6 +661,8 @@ struct i915_fbc { struct drm_mm_node compressed_fb; struct drm_mm_node *compressed_llb; + bool false_color; + struct intel_fbc_work { struct delayed_work work; struct drm_crtc *crtc; @@ -688,6 +716,7 @@ enum intel_sbi_destination { #define QUIRK_LVDS_SSC_DISABLE (1<<1) #define QUIRK_INVERT_BRIGHTNESS (1<<2) #define QUIRK_BACKLIGHT_PRESENT (1<<3) +#define QUIRK_PIPEB_FORCE (1<<4) struct intel_fbdev; struct intel_fbc_work; @@ -1147,6 +1176,7 @@ struct i915_gem_mm { }; struct drm_i915_error_state_buf { + struct drm_i915_private *i915; unsigned bytes; unsigned size; int err; @@ -1219,6 +1249,9 @@ struct i915_gpu_error { /* For missed irq/seqno simulation. */ unsigned int test_irq_rings; + + /* Used to prevent gem_check_wedged returning -EAGAIN during gpu reset */ + bool reload_in_reset; }; enum modeset_restore { @@ -1228,6 +1261,12 @@ enum modeset_restore { }; struct ddi_vbt_port_info { + /* + * This is an index in the HDMI/DVI DDI buffer translation table. + * The special value HDMI_LEVEL_SHIFT_UNKNOWN means the VBT didn't + * populate this field. + */ +#define HDMI_LEVEL_SHIFT_UNKNOWN 0xff uint8_t hdmi_level_shift; uint8_t supports_dvi:1; @@ -1421,7 +1460,7 @@ struct drm_i915_private { struct drm_i915_gem_object *semaphore_obj; uint32_t last_seqno, next_seqno; - drm_dma_handle_t *status_page_dmah; + struct drm_dma_handle *status_page_dmah; struct resource mch_res; /* protects the irq masks */ @@ -1475,6 +1514,9 @@ struct drm_i915_private { /* LVDS info */ bool no_aux_handshake; + /* protects panel power sequencer state */ + struct mutex pps_mutex; + struct drm_i915_fence_reg fence_regs[I915_MAX_NUM_FENCES]; /* assume 965 */ int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */ int num_fence_regs; /* 8 on pre-965, 16 otherwise */ @@ -1526,6 +1568,20 @@ struct drm_i915_private { struct intel_shared_dpll shared_dplls[I915_NUM_PLLS]; int dpio_phy_iosf_port[I915_NUM_PHYS_VLV]; + /* + * workarounds are currently applied at different places and + * changes are being done to consolidate them so exact count is + * not clear at this point, use a max value for now. + */ +#define I915_MAX_WA_REGS 16 + struct { + u32 addr; + u32 value; + /* bitmask representing WA bits */ + u32 mask; + } intel_wa_regs[I915_MAX_WA_REGS]; + u32 num_wa_regs; + /* Reclocking support */ bool render_reclock_avail; bool lvds_downclock_avail; @@ -1561,14 +1617,9 @@ struct drm_i915_private { #ifdef CONFIG_DRM_I915_FBDEV /* list of fbdev register on this device */ struct intel_fbdev *fbdev; + struct work_struct fbdev_suspend_work; #endif - /* - * The console may be contended at resume, but we don't - * want it to block on it. - */ - struct work_struct console_resume_work; - struct drm_property *broadcast_rgb_property; struct drm_property *force_audio_property; @@ -1614,12 +1665,28 @@ struct drm_i915_private { */ struct workqueue_struct *dp_wq; + uint32_t bios_vgacntr; + /* Old dri1 support infrastructure, beware the dragons ya fools entering * here! */ struct i915_dri1_state dri1; /* Old ums support infrastructure, same warning applies. */ struct i915_ums_state ums; + /* Abstract the submission mechanism (legacy ringbuffer or execlists) away */ + struct { + int (*do_execbuf)(struct drm_device *dev, struct drm_file *file, + struct intel_engine_cs *ring, + struct intel_context *ctx, + struct drm_i915_gem_execbuffer2 *args, + struct list_head *vmas, + struct drm_i915_gem_object *batch_obj, + u64 exec_start, u32 flags); + int (*init_rings)(struct drm_device *dev); + void (*cleanup_ring)(struct intel_engine_cs *ring); + void (*stop_ring)(struct intel_engine_cs *ring); + } gt; + /* * NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch * will be rejected. Instead look for a better place. @@ -1761,13 +1828,6 @@ struct drm_i915_gem_object { * Only honoured if hardware has relevant pte bit */ unsigned long gt_ro:1; - - /* - * Is the GPU currently using a fence to access this buffer, - */ - unsigned int pending_fenced_gpu_access:1; - unsigned int fenced_gpu_access:1; - unsigned int cache_level:3; unsigned int has_aliasing_ppgtt_mapping:1; @@ -1805,7 +1865,7 @@ struct drm_i915_gem_object { struct drm_file *pin_filp; /** for phy allocated objects */ - drm_dma_handle_t *phys_handle; + struct drm_dma_handle *phys_handle; union { struct i915_gem_userptr { @@ -1971,51 +2031,63 @@ struct drm_i915_cmd_table { int count; }; -#define INTEL_INFO(dev) (&to_i915(dev)->info) - -#define IS_I830(dev) ((dev)->pdev->device == 0x3577) -#define IS_845G(dev) ((dev)->pdev->device == 0x2562) +/* Note that the (struct drm_i915_private *) cast is just to shut up gcc. */ +#define __I915__(p) ({ \ + struct drm_i915_private *__p; \ + if (__builtin_types_compatible_p(typeof(*p), struct drm_i915_private)) \ + __p = (struct drm_i915_private *)p; \ + else if (__builtin_types_compatible_p(typeof(*p), struct drm_device)) \ + __p = to_i915((struct drm_device *)p); \ + else \ + BUILD_BUG(); \ + __p; \ +}) +#define INTEL_INFO(p) (&__I915__(p)->info) +#define INTEL_DEVID(p) (INTEL_INFO(p)->device_id) + +#define IS_I830(dev) (INTEL_DEVID(dev) == 0x3577) +#define IS_845G(dev) (INTEL_DEVID(dev) == 0x2562) #define IS_I85X(dev) (INTEL_INFO(dev)->is_i85x) -#define IS_I865G(dev) ((dev)->pdev->device == 0x2572) +#define IS_I865G(dev) (INTEL_DEVID(dev) == 0x2572) #define IS_I915G(dev) (INTEL_INFO(dev)->is_i915g) -#define IS_I915GM(dev) ((dev)->pdev->device == 0x2592) -#define IS_I945G(dev) ((dev)->pdev->device == 0x2772) +#define IS_I915GM(dev) (INTEL_DEVID(dev) == 0x2592) +#define IS_I945G(dev) (INTEL_DEVID(dev) == 0x2772) #define IS_I945GM(dev) (INTEL_INFO(dev)->is_i945gm) #define IS_BROADWATER(dev) (INTEL_INFO(dev)->is_broadwater) #define IS_CRESTLINE(dev) (INTEL_INFO(dev)->is_crestline) -#define IS_GM45(dev) ((dev)->pdev->device == 0x2A42) +#define IS_GM45(dev) (INTEL_DEVID(dev) == 0x2A42) #define IS_G4X(dev) (INTEL_INFO(dev)->is_g4x) -#define IS_PINEVIEW_G(dev) ((dev)->pdev->device == 0xa001) -#define IS_PINEVIEW_M(dev) ((dev)->pdev->device == 0xa011) +#define IS_PINEVIEW_G(dev) (INTEL_DEVID(dev) == 0xa001) +#define IS_PINEVIEW_M(dev) (INTEL_DEVID(dev) == 0xa011) #define IS_PINEVIEW(dev) (INTEL_INFO(dev)->is_pineview) #define IS_G33(dev) (INTEL_INFO(dev)->is_g33) -#define IS_IRONLAKE_M(dev) ((dev)->pdev->device == 0x0046) +#define IS_IRONLAKE_M(dev) (INTEL_DEVID(dev) == 0x0046) #define IS_IVYBRIDGE(dev) (INTEL_INFO(dev)->is_ivybridge) -#define IS_IVB_GT1(dev) ((dev)->pdev->device == 0x0156 || \ - (dev)->pdev->device == 0x0152 || \ - (dev)->pdev->device == 0x015a) -#define IS_SNB_GT1(dev) ((dev)->pdev->device == 0x0102 || \ - (dev)->pdev->device == 0x0106 || \ - (dev)->pdev->device == 0x010A) +#define IS_IVB_GT1(dev) (INTEL_DEVID(dev) == 0x0156 || \ + INTEL_DEVID(dev) == 0x0152 || \ + INTEL_DEVID(dev) == 0x015a) +#define IS_SNB_GT1(dev) (INTEL_DEVID(dev) == 0x0102 || \ + INTEL_DEVID(dev) == 0x0106 || \ + INTEL_DEVID(dev) == 0x010A) #define IS_VALLEYVIEW(dev) (INTEL_INFO(dev)->is_valleyview) #define IS_CHERRYVIEW(dev) (INTEL_INFO(dev)->is_valleyview && IS_GEN8(dev)) #define IS_HASWELL(dev) (INTEL_INFO(dev)->is_haswell) #define IS_BROADWELL(dev) (!INTEL_INFO(dev)->is_valleyview && IS_GEN8(dev)) #define IS_MOBILE(dev) (INTEL_INFO(dev)->is_mobile) #define IS_HSW_EARLY_SDV(dev) (IS_HASWELL(dev) && \ - ((dev)->pdev->device & 0xFF00) == 0x0C00) + (INTEL_DEVID(dev) & 0xFF00) == 0x0C00) #define IS_BDW_ULT(dev) (IS_BROADWELL(dev) && \ - (((dev)->pdev->device & 0xf) == 0x2 || \ - ((dev)->pdev->device & 0xf) == 0x6 || \ - ((dev)->pdev->device & 0xf) == 0xe)) + ((INTEL_DEVID(dev) & 0xf) == 0x2 || \ + (INTEL_DEVID(dev) & 0xf) == 0x6 || \ + (INTEL_DEVID(dev) & 0xf) == 0xe)) #define IS_HSW_ULT(dev) (IS_HASWELL(dev) && \ - ((dev)->pdev->device & 0xFF00) == 0x0A00) + (INTEL_DEVID(dev) & 0xFF00) == 0x0A00) #define IS_ULT(dev) (IS_HSW_ULT(dev) || IS_BDW_ULT(dev)) #define IS_HSW_GT3(dev) (IS_HASWELL(dev) && \ - ((dev)->pdev->device & 0x00F0) == 0x0020) + (INTEL_DEVID(dev) & 0x00F0) == 0x0020) /* ULX machines are also considered ULT. */ -#define IS_HSW_ULX(dev) ((dev)->pdev->device == 0x0A0E || \ - (dev)->pdev->device == 0x0A1E) +#define IS_HSW_ULX(dev) (INTEL_DEVID(dev) == 0x0A0E || \ + INTEL_DEVID(dev) == 0x0A1E) #define IS_PRELIMINARY_HW(intel_info) ((intel_info)->is_preliminary) /* @@ -2047,10 +2119,11 @@ struct drm_i915_cmd_table { #define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws) #define HAS_HW_CONTEXTS(dev) (INTEL_INFO(dev)->gen >= 6) +#define HAS_LOGICAL_RING_CONTEXTS(dev) (INTEL_INFO(dev)->gen >= 8) #define HAS_ALIASING_PPGTT(dev) (INTEL_INFO(dev)->gen >= 6) #define HAS_PPGTT(dev) (INTEL_INFO(dev)->gen >= 7 && !IS_GEN8(dev)) -#define USES_PPGTT(dev) intel_enable_ppgtt(dev, false) -#define USES_FULL_PPGTT(dev) intel_enable_ppgtt(dev, true) +#define USES_PPGTT(dev) (i915.enable_ppgtt) +#define USES_FULL_PPGTT(dev) (i915.enable_ppgtt == 2) #define HAS_OVERLAY(dev) (INTEL_INFO(dev)->has_overlay) #define OVERLAY_NEEDS_PHYSICAL(dev) (INTEL_INFO(dev)->overlay_needs_physical) @@ -2134,6 +2207,7 @@ struct i915_params { int enable_rc6; int enable_fbc; int enable_ppgtt; + int enable_execlists; int enable_psr; unsigned int preliminary_hw_support; int disable_power_well; @@ -2180,8 +2254,6 @@ extern void i915_update_gfx_val(struct drm_i915_private *dev_priv); int vlv_force_gfx_clock(struct drm_i915_private *dev_priv, bool on); void intel_hpd_cancel_work(struct drm_i915_private *dev_priv); -extern void intel_console_resume(struct work_struct *work); - /* i915_irq.c */ void i915_queue_hangcheck(struct drm_device *dev); __printf(3, 4) @@ -2229,6 +2301,20 @@ int i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +void i915_gem_execbuffer_move_to_active(struct list_head *vmas, + struct intel_engine_cs *ring); +void i915_gem_execbuffer_retire_commands(struct drm_device *dev, + struct drm_file *file, + struct intel_engine_cs *ring, + struct drm_i915_gem_object *obj); +int i915_gem_ringbuffer_submission(struct drm_device *dev, + struct drm_file *file, + struct intel_engine_cs *ring, + struct intel_context *ctx, + struct drm_i915_gem_execbuffer2 *args, + struct list_head *vmas, + struct drm_i915_gem_object *batch_obj, + u64 exec_start, u32 flags); int i915_gem_execbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_execbuffer2(struct drm_device *dev, void *data, @@ -2263,6 +2349,12 @@ int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, int i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); void i915_gem_load(struct drm_device *dev); +unsigned long i915_gem_shrink(struct drm_i915_private *dev_priv, + long target, + unsigned flags); +#define I915_SHRINK_PURGEABLE 0x1 +#define I915_SHRINK_UNBOUND 0x2 +#define I915_SHRINK_BOUND 0x4 void *i915_gem_object_alloc(struct drm_device *dev); void i915_gem_object_free(struct drm_i915_gem_object *obj); void i915_gem_object_init(struct drm_i915_gem_object *obj, @@ -2381,6 +2473,7 @@ void i915_gem_reset(struct drm_device *dev); bool i915_gem_clflush_object(struct drm_i915_gem_object *obj, bool force); int __must_check i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj); int __must_check i915_gem_init(struct drm_device *dev); +int i915_gem_init_rings(struct drm_device *dev); int __must_check i915_gem_init_hw(struct drm_device *dev); int i915_gem_l3_remap(struct intel_engine_cs *ring, int slice); void i915_gem_init_swizzling(struct drm_device *dev); @@ -2451,7 +2544,7 @@ static inline bool i915_gem_obj_is_pinned(struct drm_i915_gem_object *obj) { } /* Some GGTT VM helpers */ -#define obj_to_ggtt(obj) \ +#define i915_obj_to_ggtt(obj) \ (&((struct drm_i915_private *)(obj)->base.dev->dev_private)->gtt.base) static inline bool i915_is_ggtt(struct i915_address_space *vm) { @@ -2460,21 +2553,30 @@ static inline bool i915_is_ggtt(struct i915_address_space *vm) return vm == ggtt; } +static inline struct i915_hw_ppgtt * +i915_vm_to_ppgtt(struct i915_address_space *vm) +{ + WARN_ON(i915_is_ggtt(vm)); + + return container_of(vm, struct i915_hw_ppgtt, base); +} + + static inline bool i915_gem_obj_ggtt_bound(struct drm_i915_gem_object *obj) { - return i915_gem_obj_bound(obj, obj_to_ggtt(obj)); + return i915_gem_obj_bound(obj, i915_obj_to_ggtt(obj)); } static inline unsigned long i915_gem_obj_ggtt_offset(struct drm_i915_gem_object *obj) { - return i915_gem_obj_offset(obj, obj_to_ggtt(obj)); + return i915_gem_obj_offset(obj, i915_obj_to_ggtt(obj)); } static inline unsigned long i915_gem_obj_ggtt_size(struct drm_i915_gem_object *obj) { - return i915_gem_obj_size(obj, obj_to_ggtt(obj)); + return i915_gem_obj_size(obj, i915_obj_to_ggtt(obj)); } static inline int __must_check @@ -2482,7 +2584,8 @@ i915_gem_obj_ggtt_pin(struct drm_i915_gem_object *obj, uint32_t alignment, unsigned flags) { - return i915_gem_object_pin(obj, obj_to_ggtt(obj), alignment, flags | PIN_GLOBAL); + return i915_gem_object_pin(obj, i915_obj_to_ggtt(obj), + alignment, flags | PIN_GLOBAL); } static inline int @@ -2494,7 +2597,6 @@ i915_gem_object_ggtt_unbind(struct drm_i915_gem_object *obj) void i915_gem_object_ggtt_unpin(struct drm_i915_gem_object *obj); /* i915_gem_context.c */ -#define ctx_to_ppgtt(ctx) container_of((ctx)->vm, struct i915_hw_ppgtt, base) int __must_check i915_gem_context_init(struct drm_device *dev); void i915_gem_context_fini(struct drm_device *dev); void i915_gem_context_reset(struct drm_device *dev); @@ -2506,6 +2608,8 @@ int i915_switch_context(struct intel_engine_cs *ring, struct intel_context * i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id); void i915_gem_context_free(struct kref *ctx_ref); +struct drm_i915_gem_object * +i915_gem_alloc_context_obj(struct drm_device *dev, size_t size); static inline void i915_gem_context_reference(struct intel_context *ctx) { kref_get(&ctx->ref); @@ -2526,8 +2630,6 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file); -/* i915_gem_render_state.c */ -int i915_gem_render_state_init(struct intel_engine_cs *ring); /* i915_gem_evict.c */ int __must_check i915_gem_evict_something(struct drm_device *dev, struct i915_address_space *vm, @@ -2595,6 +2697,7 @@ void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...); int i915_error_state_to_str(struct drm_i915_error_state_buf *estr, const struct i915_error_state_file_priv *error); int i915_error_state_buf_init(struct drm_i915_error_state_buf *eb, + struct drm_i915_private *i915, size_t count, loff_t pos); static inline void i915_error_state_buf_release( struct drm_i915_error_state_buf *eb) @@ -2609,7 +2712,7 @@ void i915_error_state_put(struct i915_error_state_file_priv *error_priv); void i915_destroy_error_state(struct drm_device *dev); void i915_get_extra_instdone(struct drm_device *dev, uint32_t *instdone); -const char *i915_cache_level_str(int type); +const char *i915_cache_level_str(struct drm_i915_private *i915, int type); /* i915_cmd_parser.c */ int i915_cmd_parser_get_version(void); @@ -2701,6 +2804,7 @@ extern void intel_modeset_setup_hw_state(struct drm_device *dev, extern void i915_redisable_vga(struct drm_device *dev); extern void i915_redisable_vga_power_on(struct drm_device *dev); extern bool intel_fbc_enabled(struct drm_device *dev); +extern void gen8_fbc_sw_flush(struct drm_device *dev, u32 value); extern void intel_disable_fbc(struct drm_device *dev); extern bool ironlake_set_drps(struct drm_device *dev, u8 val); extern void intel_init_pch_refclk(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index ad55b06a3cb1c69754fe60947a183e0af6604e46..28f91df2604db0bfb867a548e701a5e9a48b7fa3 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -60,7 +60,6 @@ static unsigned long i915_gem_shrinker_scan(struct shrinker *shrinker, static int i915_gem_shrinker_oom(struct notifier_block *nb, unsigned long event, void *ptr); -static unsigned long i915_gem_purge(struct drm_i915_private *dev_priv, long target); static unsigned long i915_gem_shrink_all(struct drm_i915_private *dev_priv); static bool cpu_cache_is_coherent(struct drm_device *dev, @@ -1085,7 +1084,13 @@ i915_gem_check_wedge(struct i915_gpu_error *error, if (i915_terminally_wedged(error)) return -EIO; - return -EAGAIN; + /* + * Check if GPU Reset is in progress - we need intel_ring_begin + * to work properly to reinit the hw state while the gpu is + * still marked as reset-in-progress. Handle this with a flag. + */ + if (!error->reload_in_reset) + return -EAGAIN; } return 0; @@ -1735,7 +1740,11 @@ static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj) * offsets on purgeable objects by truncating it and marking it purged, * which prevents userspace from ever using that object again. */ - i915_gem_purge(dev_priv, obj->base.size >> PAGE_SHIFT); + i915_gem_shrink(dev_priv, + obj->base.size >> PAGE_SHIFT, + I915_SHRINK_BOUND | + I915_SHRINK_UNBOUND | + I915_SHRINK_PURGEABLE); ret = drm_gem_create_mmap_offset(&obj->base); if (ret != -ENOSPC) goto out; @@ -1932,12 +1941,11 @@ i915_gem_object_put_pages(struct drm_i915_gem_object *obj) return 0; } -static unsigned long -__i915_gem_shrink(struct drm_i915_private *dev_priv, long target, - bool purgeable_only) +unsigned long +i915_gem_shrink(struct drm_i915_private *dev_priv, + long target, unsigned flags) { - struct list_head still_in_list; - struct drm_i915_gem_object *obj; + const bool purgeable_only = flags & I915_SHRINK_PURGEABLE; unsigned long count = 0; /* @@ -1959,62 +1967,68 @@ __i915_gem_shrink(struct drm_i915_private *dev_priv, long target, * dev->struct_mutex and so we won't ever be able to observe an * object on the bound_list with a reference count equals 0. */ - INIT_LIST_HEAD(&still_in_list); - while (count < target && !list_empty(&dev_priv->mm.unbound_list)) { - obj = list_first_entry(&dev_priv->mm.unbound_list, - typeof(*obj), global_list); - list_move_tail(&obj->global_list, &still_in_list); + if (flags & I915_SHRINK_UNBOUND) { + struct list_head still_in_list; - if (!i915_gem_object_is_purgeable(obj) && purgeable_only) - continue; + INIT_LIST_HEAD(&still_in_list); + while (count < target && !list_empty(&dev_priv->mm.unbound_list)) { + struct drm_i915_gem_object *obj; - drm_gem_object_reference(&obj->base); + obj = list_first_entry(&dev_priv->mm.unbound_list, + typeof(*obj), global_list); + list_move_tail(&obj->global_list, &still_in_list); - if (i915_gem_object_put_pages(obj) == 0) - count += obj->base.size >> PAGE_SHIFT; + if (!i915_gem_object_is_purgeable(obj) && purgeable_only) + continue; - drm_gem_object_unreference(&obj->base); + drm_gem_object_reference(&obj->base); + + if (i915_gem_object_put_pages(obj) == 0) + count += obj->base.size >> PAGE_SHIFT; + + drm_gem_object_unreference(&obj->base); + } + list_splice(&still_in_list, &dev_priv->mm.unbound_list); } - list_splice(&still_in_list, &dev_priv->mm.unbound_list); - INIT_LIST_HEAD(&still_in_list); - while (count < target && !list_empty(&dev_priv->mm.bound_list)) { - struct i915_vma *vma, *v; + if (flags & I915_SHRINK_BOUND) { + struct list_head still_in_list; - obj = list_first_entry(&dev_priv->mm.bound_list, - typeof(*obj), global_list); - list_move_tail(&obj->global_list, &still_in_list); + INIT_LIST_HEAD(&still_in_list); + while (count < target && !list_empty(&dev_priv->mm.bound_list)) { + struct drm_i915_gem_object *obj; + struct i915_vma *vma, *v; - if (!i915_gem_object_is_purgeable(obj) && purgeable_only) - continue; + obj = list_first_entry(&dev_priv->mm.bound_list, + typeof(*obj), global_list); + list_move_tail(&obj->global_list, &still_in_list); - drm_gem_object_reference(&obj->base); + if (!i915_gem_object_is_purgeable(obj) && purgeable_only) + continue; - list_for_each_entry_safe(vma, v, &obj->vma_list, vma_link) - if (i915_vma_unbind(vma)) - break; + drm_gem_object_reference(&obj->base); - if (i915_gem_object_put_pages(obj) == 0) - count += obj->base.size >> PAGE_SHIFT; + list_for_each_entry_safe(vma, v, &obj->vma_list, vma_link) + if (i915_vma_unbind(vma)) + break; - drm_gem_object_unreference(&obj->base); + if (i915_gem_object_put_pages(obj) == 0) + count += obj->base.size >> PAGE_SHIFT; + + drm_gem_object_unreference(&obj->base); + } + list_splice(&still_in_list, &dev_priv->mm.bound_list); } - list_splice(&still_in_list, &dev_priv->mm.bound_list); return count; } -static unsigned long -i915_gem_purge(struct drm_i915_private *dev_priv, long target) -{ - return __i915_gem_shrink(dev_priv, target, true); -} - static unsigned long i915_gem_shrink_all(struct drm_i915_private *dev_priv) { i915_gem_evict_everything(dev_priv->dev); - return __i915_gem_shrink(dev_priv, LONG_MAX, false); + return i915_gem_shrink(dev_priv, LONG_MAX, + I915_SHRINK_BOUND | I915_SHRINK_UNBOUND); } static int @@ -2061,7 +2075,11 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj) for (i = 0; i < page_count; i++) { page = shmem_read_mapping_page_gfp(mapping, i, gfp); if (IS_ERR(page)) { - i915_gem_purge(dev_priv, page_count); + i915_gem_shrink(dev_priv, + page_count, + I915_SHRINK_BOUND | + I915_SHRINK_UNBOUND | + I915_SHRINK_PURGEABLE); page = shmem_read_mapping_page_gfp(mapping, i, gfp); } if (IS_ERR(page)) { @@ -2163,8 +2181,6 @@ static void i915_gem_object_move_to_active(struct drm_i915_gem_object *obj, struct intel_engine_cs *ring) { - struct drm_device *dev = obj->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; u32 seqno = intel_ring_get_seqno(ring); BUG_ON(ring == NULL); @@ -2183,19 +2199,6 @@ i915_gem_object_move_to_active(struct drm_i915_gem_object *obj, list_move_tail(&obj->ring_list, &ring->active_list); obj->last_read_seqno = seqno; - - if (obj->fenced_gpu_access) { - obj->last_fenced_seqno = seqno; - - /* Bump MRU to take account of the delayed flush */ - if (obj->fence_reg != I915_FENCE_REG_NONE) { - struct drm_i915_fence_reg *reg; - - reg = &dev_priv->fence_regs[obj->fence_reg]; - list_move_tail(®->lru_list, - &dev_priv->mm.fence_list); - } - } } void i915_vma_move_to_active(struct i915_vma *vma, @@ -2231,7 +2234,6 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj) obj->base.write_domain = 0; obj->last_fenced_seqno = 0; - obj->fenced_gpu_access = false; obj->active = 0; drm_gem_object_unreference(&obj->base); @@ -2329,10 +2331,21 @@ int __i915_add_request(struct intel_engine_cs *ring, { struct drm_i915_private *dev_priv = ring->dev->dev_private; struct drm_i915_gem_request *request; + struct intel_ringbuffer *ringbuf; u32 request_ring_position, request_start; int ret; - request_start = intel_ring_get_tail(ring->buffer); + request = ring->preallocated_lazy_request; + if (WARN_ON(request == NULL)) + return -ENOMEM; + + if (i915.enable_execlists) { + struct intel_context *ctx = request->ctx; + ringbuf = ctx->engine[ring->id].ringbuf; + } else + ringbuf = ring->buffer; + + request_start = intel_ring_get_tail(ringbuf); /* * Emit any outstanding flushes - execbuf can fail to emit the flush * after having emitted the batchbuffer command. Hence we need to fix @@ -2340,24 +2353,32 @@ int __i915_add_request(struct intel_engine_cs *ring, * is that the flush _must_ happen before the next request, no matter * what. */ - ret = intel_ring_flush_all_caches(ring); - if (ret) - return ret; - - request = ring->preallocated_lazy_request; - if (WARN_ON(request == NULL)) - return -ENOMEM; + if (i915.enable_execlists) { + ret = logical_ring_flush_all_caches(ringbuf); + if (ret) + return ret; + } else { + ret = intel_ring_flush_all_caches(ring); + if (ret) + return ret; + } /* Record the position of the start of the request so that * should we detect the updated seqno part-way through the * GPU processing the request, we never over-estimate the * position of the head. */ - request_ring_position = intel_ring_get_tail(ring->buffer); + request_ring_position = intel_ring_get_tail(ringbuf); - ret = ring->add_request(ring); - if (ret) - return ret; + if (i915.enable_execlists) { + ret = ring->emit_request(ringbuf); + if (ret) + return ret; + } else { + ret = ring->add_request(ring); + if (ret) + return ret; + } request->seqno = intel_ring_get_seqno(ring); request->ring = ring; @@ -2372,12 +2393,14 @@ int __i915_add_request(struct intel_engine_cs *ring, */ request->batch_obj = obj; - /* Hold a reference to the current context so that we can inspect - * it later in case a hangcheck error event fires. - */ - request->ctx = ring->last_context; - if (request->ctx) - i915_gem_context_reference(request->ctx); + if (!i915.enable_execlists) { + /* Hold a reference to the current context so that we can inspect + * it later in case a hangcheck error event fires. + */ + request->ctx = ring->last_context; + if (request->ctx) + i915_gem_context_reference(request->ctx); + } request->emitted_jiffies = jiffies; list_add_tail(&request->list, &ring->request_list); @@ -2548,6 +2571,18 @@ static void i915_gem_reset_ring_cleanup(struct drm_i915_private *dev_priv, i915_gem_free_request(request); } + while (!list_empty(&ring->execlist_queue)) { + struct intel_ctx_submit_request *submit_req; + + submit_req = list_first_entry(&ring->execlist_queue, + struct intel_ctx_submit_request, + execlist_link); + list_del(&submit_req->execlist_link); + intel_runtime_pm_put(dev_priv); + i915_gem_context_unreference(submit_req->ctx); + kfree(submit_req); + } + /* These may not have been flush before the reset, do so now */ kfree(ring->preallocated_lazy_request); ring->preallocated_lazy_request = NULL; @@ -2632,6 +2667,7 @@ i915_gem_retire_requests_ring(struct intel_engine_cs *ring) while (!list_empty(&ring->request_list)) { struct drm_i915_gem_request *request; + struct intel_ringbuffer *ringbuf; request = list_first_entry(&ring->request_list, struct drm_i915_gem_request, @@ -2641,12 +2677,24 @@ i915_gem_retire_requests_ring(struct intel_engine_cs *ring) break; trace_i915_gem_request_retire(ring, request->seqno); + + /* This is one of the few common intersection points + * between legacy ringbuffer submission and execlists: + * we need to tell them apart in order to find the correct + * ringbuffer to which the request belongs to. + */ + if (i915.enable_execlists) { + struct intel_context *ctx = request->ctx; + ringbuf = ctx->engine[ring->id].ringbuf; + } else + ringbuf = ring->buffer; + /* We know the GPU must have read the request to have * sent us the seqno + interrupt, so use the position * of tail of the request to update the last known position * of the GPU head. */ - ring->buffer->last_retired_head = request->tail; + ringbuf->last_retired_head = request->tail; i915_gem_free_request(request); } @@ -2908,6 +2956,9 @@ int i915_vma_unbind(struct i915_vma *vma) * cause memory corruption through use-after-free. */ + /* Throw away the active reference before moving to the unbound list */ + i915_gem_object_retire(obj); + if (i915_is_ggtt(vma->vm)) { i915_gem_object_finish_gtt(obj); @@ -2922,9 +2973,8 @@ int i915_vma_unbind(struct i915_vma *vma) vma->unbind_vma(vma); list_del_init(&vma->mm_list); - /* Avoid an unnecessary call to unbind on rebind. */ if (i915_is_ggtt(vma->vm)) - obj->map_and_fenceable = true; + obj->map_and_fenceable = false; drm_mm_remove_node(&vma->node); i915_gem_vma_destroy(vma); @@ -2953,9 +3003,11 @@ int i915_gpu_idle(struct drm_device *dev) /* Flush everything onto the inactive list. */ for_each_ring(ring, dev_priv, i) { - ret = i915_switch_context(ring, ring->default_context); - if (ret) - return ret; + if (!i915.enable_execlists) { + ret = i915_switch_context(ring, ring->default_context); + if (ret) + return ret; + } ret = intel_ring_idle(ring); if (ret) @@ -3169,7 +3221,6 @@ i915_gem_object_wait_fence(struct drm_i915_gem_object *obj) obj->last_fenced_seqno = 0; } - obj->fenced_gpu_access = false; return 0; } @@ -3276,6 +3327,9 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj) return 0; } } else if (enable) { + if (WARN_ON(!obj->map_and_fenceable)) + return -EINVAL; + reg = i915_find_fence_reg(dev); if (IS_ERR(reg)) return PTR_ERR(reg); @@ -3297,17 +3351,20 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj) return 0; } -static bool i915_gem_valid_gtt_space(struct drm_device *dev, - struct drm_mm_node *gtt_space, +static bool i915_gem_valid_gtt_space(struct i915_vma *vma, unsigned long cache_level) { + struct drm_mm_node *gtt_space = &vma->node; struct drm_mm_node *other; - /* On non-LLC machines we have to be careful when putting differing - * types of snoopable memory together to avoid the prefetcher - * crossing memory domains and dying. + /* + * On some machines we have to be careful when putting differing types + * of snoopable memory together to avoid the prefetcher crossing memory + * domains and dying. During vm initialisation, we decide whether or not + * these constraints apply and set the drm_mm.color_adjust + * appropriately. */ - if (HAS_LLC(dev)) + if (vma->vm->mm.color_adjust == NULL) return true; if (!drm_mm_node_allocated(gtt_space)) @@ -3445,8 +3502,7 @@ i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj, goto err_free_vma; } - if (WARN_ON(!i915_gem_valid_gtt_space(dev, &vma->node, - obj->cache_level))) { + if (WARN_ON(!i915_gem_valid_gtt_space(vma, obj->cache_level))) { ret = -EINVAL; goto err_remove_node; } @@ -3586,11 +3642,12 @@ int i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) { struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + struct i915_vma *vma = i915_gem_obj_to_ggtt(obj); uint32_t old_write_domain, old_read_domains; int ret; /* Not valid to be called on unbound objects. */ - if (!i915_gem_obj_bound_any(obj)) + if (vma == NULL) return -EINVAL; if (obj->base.write_domain == I915_GEM_DOMAIN_GTT) @@ -3632,13 +3689,9 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) old_write_domain); /* And bump the LRU for this access */ - if (i915_gem_object_is_inactive(obj)) { - struct i915_vma *vma = i915_gem_obj_to_ggtt(obj); - if (vma) - list_move_tail(&vma->mm_list, - &dev_priv->gtt.base.inactive_list); - - } + if (i915_gem_object_is_inactive(obj)) + list_move_tail(&vma->mm_list, + &dev_priv->gtt.base.inactive_list); return 0; } @@ -3659,7 +3712,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, } list_for_each_entry_safe(vma, next, &obj->vma_list, vma_link) { - if (!i915_gem_valid_gtt_space(dev, &vma->node, cache_level)) { + if (!i915_gem_valid_gtt_space(vma, cache_level)) { ret = i915_vma_unbind(vma); if (ret) return ret; @@ -3802,9 +3855,6 @@ static bool is_pin_display(struct drm_i915_gem_object *obj) { struct i915_vma *vma; - if (list_empty(&obj->vma_list)) - return false; - vma = i915_gem_obj_to_ggtt(obj); if (!vma) return false; @@ -4331,8 +4381,6 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj, obj->fence_reg = I915_FENCE_REG_NONE; obj->madv = I915_MADV_WILLNEED; - /* Avoid an unnecessary call to unbind on the first bind. */ - obj->map_and_fenceable = true; i915_gem_info_add_obj(obj->base.dev->dev_private, obj->base.size); } @@ -4493,12 +4541,18 @@ struct i915_vma *i915_gem_obj_to_vma(struct drm_i915_gem_object *obj, void i915_gem_vma_destroy(struct i915_vma *vma) { + struct i915_address_space *vm = NULL; WARN_ON(vma->node.allocated); /* Keep the vma as a placeholder in the execbuffer reservation lists */ if (!list_empty(&vma->exec_list)) return; + vm = vma->vm; + + if (!i915_is_ggtt(vm)) + i915_ppgtt_put(i915_vm_to_ppgtt(vm)); + list_del(&vma->vma_link); kfree(vma); @@ -4512,7 +4566,7 @@ i915_gem_stop_ringbuffers(struct drm_device *dev) int i; for_each_ring(ring, dev_priv, i) - intel_stop_ring_buffer(ring); + dev_priv->gt.stop_ring(ring); } int @@ -4629,11 +4683,46 @@ intel_enable_blt(struct drm_device *dev) return true; } -static int i915_gem_init_rings(struct drm_device *dev) +static void init_unused_ring(struct drm_device *dev, u32 base) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + I915_WRITE(RING_CTL(base), 0); + I915_WRITE(RING_HEAD(base), 0); + I915_WRITE(RING_TAIL(base), 0); + I915_WRITE(RING_START(base), 0); +} + +static void init_unused_rings(struct drm_device *dev) +{ + if (IS_I830(dev)) { + init_unused_ring(dev, PRB1_BASE); + init_unused_ring(dev, SRB0_BASE); + init_unused_ring(dev, SRB1_BASE); + init_unused_ring(dev, SRB2_BASE); + init_unused_ring(dev, SRB3_BASE); + } else if (IS_GEN2(dev)) { + init_unused_ring(dev, SRB0_BASE); + init_unused_ring(dev, SRB1_BASE); + } else if (IS_GEN3(dev)) { + init_unused_ring(dev, PRB1_BASE); + init_unused_ring(dev, PRB2_BASE); + } +} + +int i915_gem_init_rings(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; int ret; + /* + * At least 830 can leave some of the unused rings + * "active" (ie. head != tail) after resume which + * will prevent c3 entry. Makes sure all unused rings + * are totally idle. + */ + init_unused_rings(dev); + ret = intel_init_render_ring_buffer(dev); if (ret) return ret; @@ -4712,7 +4801,7 @@ i915_gem_init_hw(struct drm_device *dev) i915_gem_init_swizzling(dev); - ret = i915_gem_init_rings(dev); + ret = dev_priv->gt.init_rings(dev); if (ret) return ret; @@ -4730,6 +4819,14 @@ i915_gem_init_hw(struct drm_device *dev) if (ret && ret != -EIO) { DRM_ERROR("Context enable failed %d\n", ret); i915_gem_cleanup_ringbuffer(dev); + + return ret; + } + + ret = i915_ppgtt_init_hw(dev); + if (ret && ret != -EIO) { + DRM_ERROR("PPGTT enable failed %d\n", ret); + i915_gem_cleanup_ringbuffer(dev); } return ret; @@ -4740,6 +4837,9 @@ int i915_gem_init(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; int ret; + i915.enable_execlists = intel_sanitize_enable_execlists(dev, + i915.enable_execlists); + mutex_lock(&dev->struct_mutex); if (IS_VALLEYVIEW(dev)) { @@ -4750,7 +4850,24 @@ int i915_gem_init(struct drm_device *dev) DRM_DEBUG_DRIVER("allow wake ack timed out\n"); } - i915_gem_init_userptr(dev); + if (!i915.enable_execlists) { + dev_priv->gt.do_execbuf = i915_gem_ringbuffer_submission; + dev_priv->gt.init_rings = i915_gem_init_rings; + dev_priv->gt.cleanup_ring = intel_cleanup_ring_buffer; + dev_priv->gt.stop_ring = intel_stop_ring_buffer; + } else { + dev_priv->gt.do_execbuf = intel_execlists_submission; + dev_priv->gt.init_rings = intel_logical_rings_init; + dev_priv->gt.cleanup_ring = intel_logical_ring_cleanup; + dev_priv->gt.stop_ring = intel_logical_ring_stop; + } + + ret = i915_gem_init_userptr(dev); + if (ret) { + mutex_unlock(&dev->struct_mutex); + return ret; + } + i915_gem_init_global_gtt(dev); ret = i915_gem_context_init(dev); @@ -4785,7 +4902,7 @@ i915_gem_cleanup_ringbuffer(struct drm_device *dev) int i; for_each_ring(ring, dev_priv, i) - intel_cleanup_ring_buffer(ring); + dev_priv->gt.cleanup_ring(ring); } int @@ -5097,9 +5214,7 @@ unsigned long i915_gem_obj_offset(struct drm_i915_gem_object *o, struct drm_i915_private *dev_priv = o->base.dev->dev_private; struct i915_vma *vma; - if (!dev_priv->mm.aliasing_ppgtt || - vm == &dev_priv->mm.aliasing_ppgtt->base) - vm = &dev_priv->gtt.base; + WARN_ON(vm == &dev_priv->mm.aliasing_ppgtt->base); list_for_each_entry(vma, &o->vma_list, vma_link) { if (vma->vm == vm) @@ -5140,9 +5255,7 @@ unsigned long i915_gem_obj_size(struct drm_i915_gem_object *o, struct drm_i915_private *dev_priv = o->base.dev->dev_private; struct i915_vma *vma; - if (!dev_priv->mm.aliasing_ppgtt || - vm == &dev_priv->mm.aliasing_ppgtt->base) - vm = &dev_priv->gtt.base; + WARN_ON(vm == &dev_priv->mm.aliasing_ppgtt->base); BUG_ON(list_empty(&o->vma_list)); @@ -5165,11 +5278,16 @@ i915_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc) if (!i915_gem_shrinker_lock(dev, &unlock)) return SHRINK_STOP; - freed = i915_gem_purge(dev_priv, sc->nr_to_scan); + freed = i915_gem_shrink(dev_priv, + sc->nr_to_scan, + I915_SHRINK_BOUND | + I915_SHRINK_UNBOUND | + I915_SHRINK_PURGEABLE); if (freed < sc->nr_to_scan) - freed += __i915_gem_shrink(dev_priv, - sc->nr_to_scan - freed, - false); + freed += i915_gem_shrink(dev_priv, + sc->nr_to_scan - freed, + I915_SHRINK_BOUND | + I915_SHRINK_UNBOUND); if (unlock) mutex_unlock(&dev->struct_mutex); @@ -5247,14 +5365,8 @@ struct i915_vma *i915_gem_obj_to_ggtt(struct drm_i915_gem_object *obj) { struct i915_vma *vma; - /* This WARN has probably outlived its usefulness (callers already - * WARN if they don't find the GGTT vma they expect). When removing, - * remember to remove the pre-check in is_pin_display() as well */ - if (WARN_ON(list_empty(&obj->vma_list))) - return NULL; - vma = list_first_entry(&obj->vma_list, typeof(*vma), vma_link); - if (vma->vm != obj_to_ggtt(obj)) + if (vma->vm != i915_obj_to_ggtt(obj)) return NULL; return vma; diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 3b99390e467aa3bfabcfb99438d66981ec2509c0..a5221d8f1580182fe80c7c9cfc34da7d3484fe11 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -96,50 +96,6 @@ #define GEN6_CONTEXT_ALIGN (64<<10) #define GEN7_CONTEXT_ALIGN 4096 -static void do_ppgtt_cleanup(struct i915_hw_ppgtt *ppgtt) -{ - struct drm_device *dev = ppgtt->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct i915_address_space *vm = &ppgtt->base; - - if (ppgtt == dev_priv->mm.aliasing_ppgtt || - (list_empty(&vm->active_list) && list_empty(&vm->inactive_list))) { - ppgtt->base.cleanup(&ppgtt->base); - return; - } - - /* - * Make sure vmas are unbound before we take down the drm_mm - * - * FIXME: Proper refcounting should take care of this, this shouldn't be - * needed at all. - */ - if (!list_empty(&vm->active_list)) { - struct i915_vma *vma; - - list_for_each_entry(vma, &vm->active_list, mm_list) - if (WARN_ON(list_empty(&vma->vma_link) || - list_is_singular(&vma->vma_link))) - break; - - i915_gem_evict_vm(&ppgtt->base, true); - } else { - i915_gem_retire_requests(dev); - i915_gem_evict_vm(&ppgtt->base, false); - } - - ppgtt->base.cleanup(&ppgtt->base); -} - -static void ppgtt_release(struct kref *kref) -{ - struct i915_hw_ppgtt *ppgtt = - container_of(kref, struct i915_hw_ppgtt, ref); - - do_ppgtt_cleanup(ppgtt); - kfree(ppgtt); -} - static size_t get_context_alignment(struct drm_device *dev) { if (IS_GEN6(dev)) @@ -179,24 +135,20 @@ static int get_context_size(struct drm_device *dev) void i915_gem_context_free(struct kref *ctx_ref) { struct intel_context *ctx = container_of(ctx_ref, - typeof(*ctx), ref); - struct i915_hw_ppgtt *ppgtt = NULL; + typeof(*ctx), ref); - if (ctx->legacy_hw_ctx.rcs_state) { - /* We refcount even the aliasing PPGTT to keep the code symmetric */ - if (USES_PPGTT(ctx->legacy_hw_ctx.rcs_state->base.dev)) - ppgtt = ctx_to_ppgtt(ctx); - } + if (i915.enable_execlists) + intel_lr_context_free(ctx); + + i915_ppgtt_put(ctx->ppgtt); - if (ppgtt) - kref_put(&ppgtt->ref, ppgtt_release); if (ctx->legacy_hw_ctx.rcs_state) drm_gem_object_unreference(&ctx->legacy_hw_ctx.rcs_state->base); list_del(&ctx->link); kfree(ctx); } -static struct drm_i915_gem_object * +struct drm_i915_gem_object * i915_gem_alloc_context_obj(struct drm_device *dev, size_t size) { struct drm_i915_gem_object *obj; @@ -226,29 +178,9 @@ i915_gem_alloc_context_obj(struct drm_device *dev, size_t size) return obj; } -static struct i915_hw_ppgtt * -create_vm_for_ctx(struct drm_device *dev, struct intel_context *ctx) -{ - struct i915_hw_ppgtt *ppgtt; - int ret; - - ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL); - if (!ppgtt) - return ERR_PTR(-ENOMEM); - - ret = i915_gem_init_ppgtt(dev, ppgtt); - if (ret) { - kfree(ppgtt); - return ERR_PTR(ret); - } - - ppgtt->ctx = ctx; - return ppgtt; -} - static struct intel_context * __create_hw_context(struct drm_device *dev, - struct drm_i915_file_private *file_priv) + struct drm_i915_file_private *file_priv) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_context *ctx; @@ -301,11 +233,9 @@ __create_hw_context(struct drm_device *dev, */ static struct intel_context * i915_gem_create_context(struct drm_device *dev, - struct drm_i915_file_private *file_priv, - bool create_vm) + struct drm_i915_file_private *file_priv) { const bool is_global_default_ctx = file_priv == NULL; - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_context *ctx; int ret = 0; @@ -331,34 +261,18 @@ i915_gem_create_context(struct drm_device *dev, } } - if (create_vm) { - struct i915_hw_ppgtt *ppgtt = create_vm_for_ctx(dev, ctx); + if (USES_FULL_PPGTT(dev)) { + struct i915_hw_ppgtt *ppgtt = i915_ppgtt_create(dev, file_priv); if (IS_ERR_OR_NULL(ppgtt)) { DRM_DEBUG_DRIVER("PPGTT setup failed (%ld)\n", PTR_ERR(ppgtt)); ret = PTR_ERR(ppgtt); goto err_unpin; - } else - ctx->vm = &ppgtt->base; - - /* This case is reserved for the global default context and - * should only happen once. */ - if (is_global_default_ctx) { - if (WARN_ON(dev_priv->mm.aliasing_ppgtt)) { - ret = -EEXIST; - goto err_unpin; - } - - dev_priv->mm.aliasing_ppgtt = ppgtt; } - } else if (USES_PPGTT(dev)) { - /* For platforms which only have aliasing PPGTT, we fake the - * address space and refcounting. */ - ctx->vm = &dev_priv->mm.aliasing_ppgtt->base; - kref_get(&dev_priv->mm.aliasing_ppgtt->ref); - } else - ctx->vm = &dev_priv->gtt.base; + + ctx->ppgtt = ppgtt; + } return ctx; @@ -375,34 +289,23 @@ void i915_gem_context_reset(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; int i; - /* Prevent the hardware from restoring the last context (which hung) on - * the next switch */ + /* In execlists mode we will unreference the context when the execlist + * queue is cleared and the requests destroyed. + */ + if (i915.enable_execlists) + return; + for (i = 0; i < I915_NUM_RINGS; i++) { struct intel_engine_cs *ring = &dev_priv->ring[i]; - struct intel_context *dctx = ring->default_context; struct intel_context *lctx = ring->last_context; - /* Do a fake switch to the default context */ - if (lctx == dctx) - continue; - - if (!lctx) - continue; + if (lctx) { + if (lctx->legacy_hw_ctx.rcs_state && i == RCS) + i915_gem_object_ggtt_unpin(lctx->legacy_hw_ctx.rcs_state); - if (dctx->legacy_hw_ctx.rcs_state && i == RCS) { - WARN_ON(i915_gem_obj_ggtt_pin(dctx->legacy_hw_ctx.rcs_state, - get_context_alignment(dev), 0)); - /* Fake a finish/inactive */ - dctx->legacy_hw_ctx.rcs_state->base.write_domain = 0; - dctx->legacy_hw_ctx.rcs_state->active = 0; + i915_gem_context_unreference(lctx); + ring->last_context = NULL; } - - if (lctx->legacy_hw_ctx.rcs_state && i == RCS) - i915_gem_object_ggtt_unpin(lctx->legacy_hw_ctx.rcs_state); - - i915_gem_context_unreference(lctx); - i915_gem_context_reference(dctx); - ring->last_context = dctx; } } @@ -417,7 +320,11 @@ int i915_gem_context_init(struct drm_device *dev) if (WARN_ON(dev_priv->ring[RCS].default_context)) return 0; - if (HAS_HW_CONTEXTS(dev)) { + if (i915.enable_execlists) { + /* NB: intentionally left blank. We will allocate our own + * backing objects as we need them, thank you very much */ + dev_priv->hw_context_size = 0; + } else if (HAS_HW_CONTEXTS(dev)) { dev_priv->hw_context_size = round_up(get_context_size(dev), 4096); if (dev_priv->hw_context_size > (1<<20)) { DRM_DEBUG_DRIVER("Disabling HW Contexts; invalid size %d\n", @@ -426,18 +333,23 @@ int i915_gem_context_init(struct drm_device *dev) } } - ctx = i915_gem_create_context(dev, NULL, USES_PPGTT(dev)); + ctx = i915_gem_create_context(dev, NULL); if (IS_ERR(ctx)) { DRM_ERROR("Failed to create default global context (error %ld)\n", PTR_ERR(ctx)); return PTR_ERR(ctx); } - /* NB: RCS will hold a ref for all rings */ - for (i = 0; i < I915_NUM_RINGS; i++) - dev_priv->ring[i].default_context = ctx; + for (i = 0; i < I915_NUM_RINGS; i++) { + struct intel_engine_cs *ring = &dev_priv->ring[i]; - DRM_DEBUG_DRIVER("%s context support initialized\n", dev_priv->hw_context_size ? "HW" : "fake"); + /* NB: RCS will hold a ref for all rings */ + ring->default_context = ctx; + } + + DRM_DEBUG_DRIVER("%s context support initialized\n", + i915.enable_execlists ? "LR" : + dev_priv->hw_context_size ? "HW" : "fake"); return 0; } @@ -489,19 +401,11 @@ int i915_gem_context_enable(struct drm_i915_private *dev_priv) struct intel_engine_cs *ring; int ret, i; - /* This is the only place the aliasing PPGTT gets enabled, which means - * it has to happen before we bail on reset */ - if (dev_priv->mm.aliasing_ppgtt) { - struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; - ppgtt->enable(ppgtt); - } + BUG_ON(!dev_priv->ring[RCS].default_context); - /* FIXME: We should make this work, even in reset */ - if (i915_reset_in_progress(&dev_priv->gpu_error)) + if (i915.enable_execlists) return 0; - BUG_ON(!dev_priv->ring[RCS].default_context); - for_each_ring(ring, dev_priv, i) { ret = i915_switch_context(ring, ring->default_context); if (ret) @@ -527,7 +431,7 @@ int i915_gem_context_open(struct drm_device *dev, struct drm_file *file) idr_init(&file_priv->context_idr); mutex_lock(&dev->struct_mutex); - ctx = i915_gem_create_context(dev, file_priv, USES_FULL_PPGTT(dev)); + ctx = i915_gem_create_context(dev, file_priv); mutex_unlock(&dev->struct_mutex); if (IS_ERR(ctx)) { @@ -563,6 +467,7 @@ mi_set_context(struct intel_engine_cs *ring, struct intel_context *new_context, u32 hw_flags) { + u32 flags = hw_flags | MI_MM_SPACE_GTT; int ret; /* w/a: If Flush TLB Invalidation Mode is enabled, driver must do a TLB @@ -576,6 +481,10 @@ mi_set_context(struct intel_engine_cs *ring, return ret; } + /* These flags are for resource streamer on HSW+ */ + if (!IS_HASWELL(ring->dev) && INTEL_INFO(ring->dev)->gen < 8) + flags |= (MI_SAVE_EXT_STATE_EN | MI_RESTORE_EXT_STATE_EN); + ret = intel_ring_begin(ring, 6); if (ret) return ret; @@ -589,10 +498,7 @@ mi_set_context(struct intel_engine_cs *ring, intel_ring_emit(ring, MI_NOOP); intel_ring_emit(ring, MI_SET_CONTEXT); intel_ring_emit(ring, i915_gem_obj_ggtt_offset(new_context->legacy_hw_ctx.rcs_state) | - MI_MM_SPACE_GTT | - MI_SAVE_EXT_STATE_EN | - MI_RESTORE_EXT_STATE_EN | - hw_flags); + flags); /* * w/a: MI_SET_CONTEXT must always be followed by MI_NOOP * WaMiSetContext_Hang:snb,ivb,vlv @@ -614,7 +520,6 @@ static int do_switch(struct intel_engine_cs *ring, { struct drm_i915_private *dev_priv = ring->dev->dev_private; struct intel_context *from = ring->last_context; - struct i915_hw_ppgtt *ppgtt = ctx_to_ppgtt(to); u32 hw_flags = 0; bool uninitialized = false; int ret, i; @@ -642,8 +547,8 @@ static int do_switch(struct intel_engine_cs *ring, */ from = ring->last_context; - if (USES_FULL_PPGTT(ring->dev)) { - ret = ppgtt->switch_mm(ppgtt, ring, false); + if (to->ppgtt) { + ret = to->ppgtt->switch_mm(to->ppgtt, ring); if (ret) goto unpin_out; } @@ -723,6 +628,12 @@ static int do_switch(struct intel_engine_cs *ring, ring->last_context = to; if (uninitialized) { + if (ring->init_context) { + ret = ring->init_context(ring); + if (ret) + DRM_ERROR("ring init context: %d\n", ret); + } + ret = i915_gem_render_state_init(ring); if (ret) DRM_ERROR("init render state: %d\n", ret); @@ -743,14 +654,19 @@ static int do_switch(struct intel_engine_cs *ring, * * The context life cycle is simple. The context refcount is incremented and * decremented by 1 and create and destroy. If the context is in use by the GPU, - * it will have a refoucnt > 1. This allows us to destroy the context abstract + * it will have a refcount > 1. This allows us to destroy the context abstract * object while letting the normal object tracking destroy the backing BO. + * + * This function should not be used in execlists mode. Instead the context is + * switched by writing to the ELSP and requests keep a reference to their + * context. */ int i915_switch_context(struct intel_engine_cs *ring, struct intel_context *to) { struct drm_i915_private *dev_priv = ring->dev->dev_private; + WARN_ON(i915.enable_execlists); WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex)); if (to->legacy_hw_ctx.rcs_state == NULL) { /* We have the fake context */ @@ -766,9 +682,9 @@ int i915_switch_context(struct intel_engine_cs *ring, return do_switch(ring, to); } -static bool hw_context_enabled(struct drm_device *dev) +static bool contexts_enabled(struct drm_device *dev) { - return to_i915(dev)->hw_context_size; + return i915.enable_execlists || to_i915(dev)->hw_context_size; } int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, @@ -779,14 +695,14 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, struct intel_context *ctx; int ret; - if (!hw_context_enabled(dev)) + if (!contexts_enabled(dev)) return -ENODEV; ret = i915_mutex_lock_interruptible(dev); if (ret) return ret; - ctx = i915_gem_create_context(dev, file_priv, USES_FULL_PPGTT(dev)); + ctx = i915_gem_create_context(dev, file_priv); mutex_unlock(&dev->struct_mutex); if (IS_ERR(ctx)) return PTR_ERR(ctx); diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index bbf4b12d842effa7972e7dcaaa23e1de08434b59..886ff2ee7a282e0762855e244e40cd8a7493b1fe 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -243,7 +243,7 @@ int i915_gem_evict_everything(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct i915_address_space *vm; + struct i915_address_space *vm, *v; bool lists_empty = true; int ret; @@ -270,7 +270,7 @@ i915_gem_evict_everything(struct drm_device *dev) i915_gem_retire_requests(dev); /* Having flushed everything, unbind() should never raise an error */ - list_for_each_entry(vm, &dev_priv->vm_list, global_link) + list_for_each_entry_safe(vm, v, &dev_priv->vm_list, global_link) WARN_ON(i915_gem_evict_vm(vm, false)); return 0; diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 60998fc4e5b22147687db554a4c80f27d08e1bea..1a0611bb576b3eb9b78ede149955f36b798360df 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -35,6 +35,7 @@ #define __EXEC_OBJECT_HAS_PIN (1<<31) #define __EXEC_OBJECT_HAS_FENCE (1<<30) +#define __EXEC_OBJECT_NEEDS_MAP (1<<29) #define __EXEC_OBJECT_NEEDS_BIAS (1<<28) #define BATCH_OFFSET_BIAS (256*1024) @@ -94,7 +95,6 @@ eb_lookup_vmas(struct eb_vmas *eb, struct i915_address_space *vm, struct drm_file *file) { - struct drm_i915_private *dev_priv = vm->dev->dev_private; struct drm_i915_gem_object *obj; struct list_head objects; int i, ret; @@ -129,20 +129,6 @@ eb_lookup_vmas(struct eb_vmas *eb, i = 0; while (!list_empty(&objects)) { struct i915_vma *vma; - struct i915_address_space *bind_vm = vm; - - if (exec[i].flags & EXEC_OBJECT_NEEDS_GTT && - USES_FULL_PPGTT(vm->dev)) { - ret = -EINVAL; - goto err; - } - - /* If we have secure dispatch, or the userspace assures us that - * they know what they're doing, use the GGTT VM. - */ - if (((args->flags & I915_EXEC_SECURE) && - (i == (args->buffer_count - 1)))) - bind_vm = &dev_priv->gtt.base; obj = list_first_entry(&objects, struct drm_i915_gem_object, @@ -156,7 +142,7 @@ eb_lookup_vmas(struct eb_vmas *eb, * from the (obj, vm) we don't run the risk of creating * duplicated vmas for the same vm. */ - vma = i915_gem_obj_lookup_or_create_vma(obj, bind_vm); + vma = i915_gem_obj_lookup_or_create_vma(obj, vm); if (IS_ERR(vma)) { DRM_DEBUG("Failed to lookup VMA\n"); ret = PTR_ERR(vma); @@ -307,7 +293,7 @@ relocate_entry_gtt(struct drm_i915_gem_object *obj, struct drm_device *dev = obj->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; uint64_t delta = reloc->delta + target_offset; - uint32_t __iomem *reloc_entry; + uint64_t offset; void __iomem *reloc_page; int ret; @@ -320,25 +306,24 @@ relocate_entry_gtt(struct drm_i915_gem_object *obj, return ret; /* Map the page containing the relocation we're going to perform. */ - reloc->offset += i915_gem_obj_ggtt_offset(obj); + offset = i915_gem_obj_ggtt_offset(obj); + offset += reloc->offset; reloc_page = io_mapping_map_atomic_wc(dev_priv->gtt.mappable, - reloc->offset & PAGE_MASK); - reloc_entry = (uint32_t __iomem *) - (reloc_page + offset_in_page(reloc->offset)); - iowrite32(lower_32_bits(delta), reloc_entry); + offset & PAGE_MASK); + iowrite32(lower_32_bits(delta), reloc_page + offset_in_page(offset)); if (INTEL_INFO(dev)->gen >= 8) { - reloc_entry += 1; + offset += sizeof(uint32_t); - if (offset_in_page(reloc->offset + sizeof(uint32_t)) == 0) { + if (offset_in_page(offset) == 0) { io_mapping_unmap_atomic(reloc_page); - reloc_page = io_mapping_map_atomic_wc( - dev_priv->gtt.mappable, - reloc->offset + sizeof(uint32_t)); - reloc_entry = reloc_page; + reloc_page = + io_mapping_map_atomic_wc(dev_priv->gtt.mappable, + offset); } - iowrite32(upper_32_bits(delta), reloc_entry); + iowrite32(upper_32_bits(delta), + reloc_page + offset_in_page(offset)); } io_mapping_unmap_atomic(reloc_page); @@ -534,14 +519,6 @@ i915_gem_execbuffer_relocate(struct eb_vmas *eb) return ret; } -static int -need_reloc_mappable(struct i915_vma *vma) -{ - struct drm_i915_gem_exec_object2 *entry = vma->exec_entry; - return entry->relocation_count && !use_cpu_reloc(vma->obj) && - i915_is_ggtt(vma->vm); -} - static int i915_gem_execbuffer_reserve_vma(struct i915_vma *vma, struct intel_engine_cs *ring, @@ -549,20 +526,12 @@ i915_gem_execbuffer_reserve_vma(struct i915_vma *vma, { struct drm_i915_gem_object *obj = vma->obj; struct drm_i915_gem_exec_object2 *entry = vma->exec_entry; - bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4; - bool need_fence; uint64_t flags; int ret; flags = 0; - - need_fence = - has_fenced_gpu_access && - entry->flags & EXEC_OBJECT_NEEDS_FENCE && - obj->tiling_mode != I915_TILING_NONE; - if (need_fence || need_reloc_mappable(vma)) + if (entry->flags & __EXEC_OBJECT_NEEDS_MAP) flags |= PIN_MAPPABLE; - if (entry->flags & EXEC_OBJECT_NEEDS_GTT) flags |= PIN_GLOBAL; if (entry->flags & __EXEC_OBJECT_NEEDS_BIAS) @@ -574,17 +543,13 @@ i915_gem_execbuffer_reserve_vma(struct i915_vma *vma, entry->flags |= __EXEC_OBJECT_HAS_PIN; - if (has_fenced_gpu_access) { - if (entry->flags & EXEC_OBJECT_NEEDS_FENCE) { - ret = i915_gem_object_get_fence(obj); - if (ret) - return ret; - - if (i915_gem_object_pin_fence(obj)) - entry->flags |= __EXEC_OBJECT_HAS_FENCE; + if (entry->flags & EXEC_OBJECT_NEEDS_FENCE) { + ret = i915_gem_object_get_fence(obj); + if (ret) + return ret; - obj->pending_fenced_gpu_access = true; - } + if (i915_gem_object_pin_fence(obj)) + entry->flags |= __EXEC_OBJECT_HAS_FENCE; } if (entry->offset != vma->node.start) { @@ -601,26 +566,40 @@ i915_gem_execbuffer_reserve_vma(struct i915_vma *vma, } static bool -eb_vma_misplaced(struct i915_vma *vma, bool has_fenced_gpu_access) +need_reloc_mappable(struct i915_vma *vma) { struct drm_i915_gem_exec_object2 *entry = vma->exec_entry; - struct drm_i915_gem_object *obj = vma->obj; - bool need_fence, need_mappable; - need_fence = - has_fenced_gpu_access && - entry->flags & EXEC_OBJECT_NEEDS_FENCE && - obj->tiling_mode != I915_TILING_NONE; - need_mappable = need_fence || need_reloc_mappable(vma); + if (entry->relocation_count == 0) + return false; + + if (!i915_is_ggtt(vma->vm)) + return false; + + /* See also use_cpu_reloc() */ + if (HAS_LLC(vma->obj->base.dev)) + return false; - WARN_ON((need_mappable || need_fence) && + if (vma->obj->base.write_domain == I915_GEM_DOMAIN_CPU) + return false; + + return true; +} + +static bool +eb_vma_misplaced(struct i915_vma *vma) +{ + struct drm_i915_gem_exec_object2 *entry = vma->exec_entry; + struct drm_i915_gem_object *obj = vma->obj; + + WARN_ON(entry->flags & __EXEC_OBJECT_NEEDS_MAP && !i915_is_ggtt(vma->vm)); if (entry->alignment && vma->node.start & (entry->alignment - 1)) return true; - if (need_mappable && !obj->map_and_fenceable) + if (entry->flags & __EXEC_OBJECT_NEEDS_MAP && !obj->map_and_fenceable) return true; if (entry->flags & __EXEC_OBJECT_NEEDS_BIAS && @@ -642,9 +621,6 @@ i915_gem_execbuffer_reserve(struct intel_engine_cs *ring, bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4; int retry; - if (list_empty(vmas)) - return 0; - i915_gem_retire_requests_ring(ring); vm = list_first_entry(vmas, struct i915_vma, exec_list)->vm; @@ -658,20 +634,21 @@ i915_gem_execbuffer_reserve(struct intel_engine_cs *ring, obj = vma->obj; entry = vma->exec_entry; + if (!has_fenced_gpu_access) + entry->flags &= ~EXEC_OBJECT_NEEDS_FENCE; need_fence = - has_fenced_gpu_access && entry->flags & EXEC_OBJECT_NEEDS_FENCE && obj->tiling_mode != I915_TILING_NONE; need_mappable = need_fence || need_reloc_mappable(vma); - if (need_mappable) + if (need_mappable) { + entry->flags |= __EXEC_OBJECT_NEEDS_MAP; list_move(&vma->exec_list, &ordered_vmas); - else + } else list_move_tail(&vma->exec_list, &ordered_vmas); obj->base.pending_read_domains = I915_GEM_GPU_DOMAINS & ~I915_GEM_DOMAIN_COMMAND; obj->base.pending_write_domain = 0; - obj->pending_fenced_gpu_access = false; } list_splice(&ordered_vmas, vmas); @@ -696,7 +673,7 @@ i915_gem_execbuffer_reserve(struct intel_engine_cs *ring, if (!drm_mm_node_allocated(&vma->node)) continue; - if (eb_vma_misplaced(vma, has_fenced_gpu_access)) + if (eb_vma_misplaced(vma)) ret = i915_vma_unbind(vma); else ret = i915_gem_execbuffer_reserve_vma(vma, ring, need_relocs); @@ -744,9 +721,6 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, int i, total, ret; unsigned count = args->buffer_count; - if (WARN_ON(list_empty(&eb->vmas))) - return 0; - vm = list_first_entry(&eb->vmas, struct i915_vma, exec_list)->vm; /* We may process another execbuffer during the unlock... */ @@ -890,18 +864,24 @@ i915_gem_check_execbuffer(struct drm_i915_gem_execbuffer2 *exec) } static int -validate_exec_list(struct drm_i915_gem_exec_object2 *exec, +validate_exec_list(struct drm_device *dev, + struct drm_i915_gem_exec_object2 *exec, int count) { - int i; unsigned relocs_total = 0; unsigned relocs_max = UINT_MAX / sizeof(struct drm_i915_gem_relocation_entry); + unsigned invalid_flags; + int i; + + invalid_flags = __EXEC_OBJECT_UNKNOWN_FLAGS; + if (USES_FULL_PPGTT(dev)) + invalid_flags |= EXEC_OBJECT_NEEDS_GTT; for (i = 0; i < count; i++) { char __user *ptr = to_user_ptr(exec[i].relocs_ptr); int length; /* limited by fault_in_pages_readable() */ - if (exec[i].flags & __EXEC_OBJECT_UNKNOWN_FLAGS) + if (exec[i].flags & invalid_flags) return -EINVAL; /* First check for malicious input causing overflow in @@ -951,16 +931,26 @@ i915_gem_validate_context(struct drm_device *dev, struct drm_file *file, return ERR_PTR(-EIO); } + if (i915.enable_execlists && !ctx->engine[ring->id].state) { + int ret = intel_lr_context_deferred_create(ctx, ring); + if (ret) { + DRM_DEBUG("Could not create LRC %u: %d\n", ctx_id, ret); + return ERR_PTR(ret); + } + } + return ctx; } -static void +void i915_gem_execbuffer_move_to_active(struct list_head *vmas, struct intel_engine_cs *ring) { + u32 seqno = intel_ring_get_seqno(ring); struct i915_vma *vma; list_for_each_entry(vma, vmas, exec_list) { + struct drm_i915_gem_exec_object2 *entry = vma->exec_entry; struct drm_i915_gem_object *obj = vma->obj; u32 old_read = obj->base.read_domains; u32 old_write = obj->base.write_domain; @@ -969,24 +959,31 @@ i915_gem_execbuffer_move_to_active(struct list_head *vmas, if (obj->base.write_domain == 0) obj->base.pending_read_domains |= obj->base.read_domains; obj->base.read_domains = obj->base.pending_read_domains; - obj->fenced_gpu_access = obj->pending_fenced_gpu_access; i915_vma_move_to_active(vma, ring); if (obj->base.write_domain) { obj->dirty = 1; - obj->last_write_seqno = intel_ring_get_seqno(ring); + obj->last_write_seqno = seqno; intel_fb_obj_invalidate(obj, ring); /* update for the implicit flush after a batch */ obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS; } + if (entry->flags & EXEC_OBJECT_NEEDS_FENCE) { + obj->last_fenced_seqno = seqno; + if (entry->flags & __EXEC_OBJECT_HAS_FENCE) { + struct drm_i915_private *dev_priv = to_i915(ring->dev); + list_move_tail(&dev_priv->fence_regs[obj->fence_reg].lru_list, + &dev_priv->mm.fence_list); + } + } trace_i915_gem_object_change_domain(obj, old_read, old_write); } } -static void +void i915_gem_execbuffer_retire_commands(struct drm_device *dev, struct drm_file *file, struct intel_engine_cs *ring, @@ -1026,14 +1023,14 @@ i915_reset_gen7_sol_offsets(struct drm_device *dev, return 0; } -static int -legacy_ringbuffer_submission(struct drm_device *dev, struct drm_file *file, - struct intel_engine_cs *ring, - struct intel_context *ctx, - struct drm_i915_gem_execbuffer2 *args, - struct list_head *vmas, - struct drm_i915_gem_object *batch_obj, - u64 exec_start, u32 flags) +int +i915_gem_ringbuffer_submission(struct drm_device *dev, struct drm_file *file, + struct intel_engine_cs *ring, + struct intel_context *ctx, + struct drm_i915_gem_execbuffer2 *args, + struct list_head *vmas, + struct drm_i915_gem_object *batch_obj, + u64 exec_start, u32 flags) { struct drm_clip_rect *cliprects = NULL; struct drm_i915_private *dev_priv = dev->dev_private; @@ -1254,7 +1251,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, if (!i915_gem_check_execbuffer(args)) return -EINVAL; - ret = validate_exec_list(exec, args->buffer_count); + ret = validate_exec_list(dev, exec, args->buffer_count); if (ret) return ret; @@ -1318,8 +1315,9 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, i915_gem_context_reference(ctx); - vm = ctx->vm; - if (!USES_FULL_PPGTT(dev)) + if (ctx->ppgtt) + vm = &ctx->ppgtt->base; + else vm = &dev_priv->gtt.base; eb = eb_create(args); @@ -1386,25 +1384,36 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, /* snb/ivb/vlv conflate the "batch in ppgtt" bit with the "non-secure * batch" bit. Hence we need to pin secure batches into the global gtt. * hsw should have this fixed, but bdw mucks it up again. */ - if (flags & I915_DISPATCH_SECURE && - !batch_obj->has_global_gtt_mapping) { - /* When we have multiple VMs, we'll need to make sure that we - * allocate space first */ - struct i915_vma *vma = i915_gem_obj_to_ggtt(batch_obj); - BUG_ON(!vma); - vma->bind_vma(vma, batch_obj->cache_level, GLOBAL_BIND); - } + if (flags & I915_DISPATCH_SECURE) { + /* + * So on first glance it looks freaky that we pin the batch here + * outside of the reservation loop. But: + * - The batch is already pinned into the relevant ppgtt, so we + * already have the backing storage fully allocated. + * - No other BO uses the global gtt (well contexts, but meh), + * so we don't really have issues with mutliple objects not + * fitting due to fragmentation. + * So this is actually safe. + */ + ret = i915_gem_obj_ggtt_pin(batch_obj, 0, 0); + if (ret) + goto err; - if (flags & I915_DISPATCH_SECURE) exec_start += i915_gem_obj_ggtt_offset(batch_obj); - else + } else exec_start += i915_gem_obj_offset(batch_obj, vm); - ret = legacy_ringbuffer_submission(dev, file, ring, ctx, - args, &eb->vmas, batch_obj, exec_start, flags); - if (ret) - goto err; + ret = dev_priv->gt.do_execbuf(dev, file, ring, ctx, args, + &eb->vmas, batch_obj, exec_start, flags); + /* + * FIXME: We crucially rely upon the active tracking for the (ppgtt) + * batch vma for correctness. For less ugly and less fragility this + * needs to be adjusted to also track the ggtt batch vma properly as + * active. + */ + if (flags & I915_DISPATCH_SECURE) + i915_gem_object_ggtt_unpin(batch_obj); err: /* the request owns the ref now */ i915_gem_context_unreference(ctx); diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index e42925f76b4bb807393040e60c809fa8bc84e096..b672b843fd5e5831323094824116cffe912d8a55 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -33,17 +33,6 @@ static void bdw_setup_private_ppat(struct drm_i915_private *dev_priv); static void chv_setup_private_ppat(struct drm_i915_private *dev_priv); -bool intel_enable_ppgtt(struct drm_device *dev, bool full) -{ - if (i915.enable_ppgtt == 0) - return false; - - if (i915.enable_ppgtt == 1 && full) - return false; - - return true; -} - static int sanitize_enable_ppgtt(struct drm_device *dev, int enable_ppgtt) { if (enable_ppgtt == 0 || !HAS_ALIASING_PPGTT(dev)) @@ -78,7 +67,6 @@ static void ppgtt_bind_vma(struct i915_vma *vma, enum i915_cache_level cache_level, u32 flags); static void ppgtt_unbind_vma(struct i915_vma *vma); -static int gen8_ppgtt_enable(struct i915_hw_ppgtt *ppgtt); static inline gen8_gtt_pte_t gen8_pte_encode(dma_addr_t addr, enum i915_cache_level level, @@ -216,19 +204,12 @@ static gen6_gtt_pte_t iris_pte_encode(dma_addr_t addr, /* Broadwell Page Directory Pointer Descriptors */ static int gen8_write_pdp(struct intel_engine_cs *ring, unsigned entry, - uint64_t val, bool synchronous) + uint64_t val) { - struct drm_i915_private *dev_priv = ring->dev->dev_private; int ret; BUG_ON(entry >= 4); - if (synchronous) { - I915_WRITE(GEN8_RING_PDP_UDW(ring, entry), val >> 32); - I915_WRITE(GEN8_RING_PDP_LDW(ring, entry), (u32)val); - return 0; - } - ret = intel_ring_begin(ring, 6); if (ret) return ret; @@ -245,8 +226,7 @@ static int gen8_write_pdp(struct intel_engine_cs *ring, unsigned entry, } static int gen8_mm_switch(struct i915_hw_ppgtt *ppgtt, - struct intel_engine_cs *ring, - bool synchronous) + struct intel_engine_cs *ring) { int i, ret; @@ -255,7 +235,7 @@ static int gen8_mm_switch(struct i915_hw_ppgtt *ppgtt, for (i = used_pd - 1; i >= 0; i--) { dma_addr_t addr = ppgtt->pd_dma_addr[i]; - ret = gen8_write_pdp(ring, i, addr, synchronous); + ret = gen8_write_pdp(ring, i, addr); if (ret) return ret; } @@ -403,9 +383,6 @@ static void gen8_ppgtt_cleanup(struct i915_address_space *vm) struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); - list_del(&vm->global_link); - drm_mm_takedown(&vm->mm); - gen8_ppgtt_unmap_pages(ppgtt); gen8_ppgtt_free(ppgtt); } @@ -615,7 +592,6 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt, uint64_t size) kunmap_atomic(pd_vaddr); } - ppgtt->enable = gen8_ppgtt_enable; ppgtt->switch_mm = gen8_mm_switch; ppgtt->base.clear_range = gen8_ppgtt_clear_range; ppgtt->base.insert_entries = gen8_ppgtt_insert_entries; @@ -724,29 +700,10 @@ static uint32_t get_pd_offset(struct i915_hw_ppgtt *ppgtt) } static int hsw_mm_switch(struct i915_hw_ppgtt *ppgtt, - struct intel_engine_cs *ring, - bool synchronous) + struct intel_engine_cs *ring) { - struct drm_device *dev = ppgtt->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; int ret; - /* If we're in reset, we can assume the GPU is sufficiently idle to - * manually frob these bits. Ideally we could use the ring functions, - * except our error handling makes it quite difficult (can't use - * intel_ring_begin, ring->flush, or intel_ring_advance) - * - * FIXME: We should try not to special case reset - */ - if (synchronous || - i915_reset_in_progress(&dev_priv->gpu_error)) { - WARN_ON(ppgtt != dev_priv->mm.aliasing_ppgtt); - I915_WRITE(RING_PP_DIR_DCLV(ring), PP_DIR_DCLV_2G); - I915_WRITE(RING_PP_DIR_BASE(ring), get_pd_offset(ppgtt)); - POSTING_READ(RING_PP_DIR_BASE(ring)); - return 0; - } - /* NB: TLBs must be flushed and invalidated before a switch */ ret = ring->flush(ring, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS); if (ret) @@ -768,29 +725,10 @@ static int hsw_mm_switch(struct i915_hw_ppgtt *ppgtt, } static int gen7_mm_switch(struct i915_hw_ppgtt *ppgtt, - struct intel_engine_cs *ring, - bool synchronous) + struct intel_engine_cs *ring) { - struct drm_device *dev = ppgtt->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; int ret; - /* If we're in reset, we can assume the GPU is sufficiently idle to - * manually frob these bits. Ideally we could use the ring functions, - * except our error handling makes it quite difficult (can't use - * intel_ring_begin, ring->flush, or intel_ring_advance) - * - * FIXME: We should try not to special case reset - */ - if (synchronous || - i915_reset_in_progress(&dev_priv->gpu_error)) { - WARN_ON(ppgtt != dev_priv->mm.aliasing_ppgtt); - I915_WRITE(RING_PP_DIR_DCLV(ring), PP_DIR_DCLV_2G); - I915_WRITE(RING_PP_DIR_BASE(ring), get_pd_offset(ppgtt)); - POSTING_READ(RING_PP_DIR_BASE(ring)); - return 0; - } - /* NB: TLBs must be flushed and invalidated before a switch */ ret = ring->flush(ring, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS); if (ret) @@ -819,14 +757,11 @@ static int gen7_mm_switch(struct i915_hw_ppgtt *ppgtt, } static int gen6_mm_switch(struct i915_hw_ppgtt *ppgtt, - struct intel_engine_cs *ring, - bool synchronous) + struct intel_engine_cs *ring) { struct drm_device *dev = ppgtt->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - if (!synchronous) - return 0; I915_WRITE(RING_PP_DIR_DCLV(ring), PP_DIR_DCLV_2G); I915_WRITE(RING_PP_DIR_BASE(ring), get_pd_offset(ppgtt)); @@ -836,39 +771,20 @@ static int gen6_mm_switch(struct i915_hw_ppgtt *ppgtt, return 0; } -static int gen8_ppgtt_enable(struct i915_hw_ppgtt *ppgtt) +static void gen8_ppgtt_enable(struct drm_device *dev) { - struct drm_device *dev = ppgtt->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_engine_cs *ring; - int j, ret; + int j; for_each_ring(ring, dev_priv, j) { I915_WRITE(RING_MODE_GEN7(ring), _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE)); - - /* We promise to do a switch later with FULL PPGTT. If this is - * aliasing, this is the one and only switch we'll do */ - if (USES_FULL_PPGTT(dev)) - continue; - - ret = ppgtt->switch_mm(ppgtt, ring, true); - if (ret) - goto err_out; } - - return 0; - -err_out: - for_each_ring(ring, dev_priv, j) - I915_WRITE(RING_MODE_GEN7(ring), - _MASKED_BIT_DISABLE(GFX_PPGTT_ENABLE)); - return ret; } -static int gen7_ppgtt_enable(struct i915_hw_ppgtt *ppgtt) +static void gen7_ppgtt_enable(struct drm_device *dev) { - struct drm_device *dev = ppgtt->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_engine_cs *ring; uint32_t ecochk, ecobits; @@ -887,31 +803,16 @@ static int gen7_ppgtt_enable(struct i915_hw_ppgtt *ppgtt) I915_WRITE(GAM_ECOCHK, ecochk); for_each_ring(ring, dev_priv, i) { - int ret; /* GFX_MODE is per-ring on gen7+ */ I915_WRITE(RING_MODE_GEN7(ring), _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE)); - - /* We promise to do a switch later with FULL PPGTT. If this is - * aliasing, this is the one and only switch we'll do */ - if (USES_FULL_PPGTT(dev)) - continue; - - ret = ppgtt->switch_mm(ppgtt, ring, true); - if (ret) - return ret; } - - return 0; } -static int gen6_ppgtt_enable(struct i915_hw_ppgtt *ppgtt) +static void gen6_ppgtt_enable(struct drm_device *dev) { - struct drm_device *dev = ppgtt->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_engine_cs *ring; uint32_t ecochk, gab_ctl, ecobits; - int i; ecobits = I915_READ(GAC_ECO_BITS); I915_WRITE(GAC_ECO_BITS, ecobits | ECOBITS_SNB_BIT | @@ -924,14 +825,6 @@ static int gen6_ppgtt_enable(struct i915_hw_ppgtt *ppgtt) I915_WRITE(GAM_ECOCHK, ecochk | ECOCHK_SNB_BIT | ECOCHK_PPGTT_CACHE64B); I915_WRITE(GFX_MODE, _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE)); - - for_each_ring(ring, dev_priv, i) { - int ret = ppgtt->switch_mm(ppgtt, ring, true); - if (ret) - return ret; - } - - return 0; } /* PPGTT support for Sandybdrige/Gen6 and later */ @@ -1029,8 +922,6 @@ static void gen6_ppgtt_cleanup(struct i915_address_space *vm) struct i915_hw_ppgtt *ppgtt = container_of(vm, struct i915_hw_ppgtt, base); - list_del(&vm->global_link); - drm_mm_takedown(&ppgtt->base.mm); drm_mm_remove_node(&ppgtt->node); gen6_ppgtt_unmap_pages(ppgtt); @@ -1151,13 +1042,10 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt) ppgtt->base.pte_encode = dev_priv->gtt.base.pte_encode; if (IS_GEN6(dev)) { - ppgtt->enable = gen6_ppgtt_enable; ppgtt->switch_mm = gen6_mm_switch; } else if (IS_HASWELL(dev)) { - ppgtt->enable = gen7_ppgtt_enable; ppgtt->switch_mm = hsw_mm_switch; } else if (IS_GEN7(dev)) { - ppgtt->enable = gen7_ppgtt_enable; ppgtt->switch_mm = gen7_mm_switch; } else BUG(); @@ -1188,39 +1076,114 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt) ppgtt->node.size >> 20, ppgtt->node.start / PAGE_SIZE); + gen6_write_pdes(ppgtt); + DRM_DEBUG("Adding PPGTT at offset %x\n", + ppgtt->pd_offset << 10); + return 0; } -int i915_gem_init_ppgtt(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt) +static int __hw_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt) { struct drm_i915_private *dev_priv = dev->dev_private; - int ret = 0; ppgtt->base.dev = dev; ppgtt->base.scratch = dev_priv->gtt.base.scratch; if (INTEL_INFO(dev)->gen < 8) - ret = gen6_ppgtt_init(ppgtt); + return gen6_ppgtt_init(ppgtt); else if (IS_GEN8(dev)) - ret = gen8_ppgtt_init(ppgtt, dev_priv->gtt.base.total); + return gen8_ppgtt_init(ppgtt, dev_priv->gtt.base.total); else BUG(); +} +int i915_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int ret = 0; - if (!ret) { - struct drm_i915_private *dev_priv = dev->dev_private; + ret = __hw_ppgtt_init(dev, ppgtt); + if (ret == 0) { kref_init(&ppgtt->ref); drm_mm_init(&ppgtt->base.mm, ppgtt->base.start, ppgtt->base.total); i915_init_vm(dev_priv, &ppgtt->base); - if (INTEL_INFO(dev)->gen < 8) { - gen6_write_pdes(ppgtt); - DRM_DEBUG("Adding PPGTT at offset %x\n", - ppgtt->pd_offset << 10); + } + + return ret; +} + +int i915_ppgtt_init_hw(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_engine_cs *ring; + struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; + int i, ret = 0; + + /* In the case of execlists, PPGTT is enabled by the context descriptor + * and the PDPs are contained within the context itself. We don't + * need to do anything here. */ + if (i915.enable_execlists) + return 0; + + if (!USES_PPGTT(dev)) + return 0; + + if (IS_GEN6(dev)) + gen6_ppgtt_enable(dev); + else if (IS_GEN7(dev)) + gen7_ppgtt_enable(dev); + else if (INTEL_INFO(dev)->gen >= 8) + gen8_ppgtt_enable(dev); + else + WARN_ON(1); + + if (ppgtt) { + for_each_ring(ring, dev_priv, i) { + ret = ppgtt->switch_mm(ppgtt, ring); + if (ret != 0) + return ret; } } return ret; } +struct i915_hw_ppgtt * +i915_ppgtt_create(struct drm_device *dev, struct drm_i915_file_private *fpriv) +{ + struct i915_hw_ppgtt *ppgtt; + int ret; + + ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL); + if (!ppgtt) + return ERR_PTR(-ENOMEM); + + ret = i915_ppgtt_init(dev, ppgtt); + if (ret) { + kfree(ppgtt); + return ERR_PTR(ret); + } + + ppgtt->file_priv = fpriv; + + return ppgtt; +} + +void i915_ppgtt_release(struct kref *kref) +{ + struct i915_hw_ppgtt *ppgtt = + container_of(kref, struct i915_hw_ppgtt, ref); + + /* vmas should already be unbound */ + WARN_ON(!list_empty(&ppgtt->base.active_list)); + WARN_ON(!list_empty(&ppgtt->base.inactive_list)); + + list_del(&ppgtt->base.global_link); + drm_mm_takedown(&ppgtt->base.mm); + + ppgtt->base.cleanup(&ppgtt->base); + kfree(ppgtt); +} static void ppgtt_bind_vma(struct i915_vma *vma, @@ -1687,10 +1650,10 @@ static void i915_gtt_color_adjust(struct drm_mm_node *node, } } -void i915_gem_setup_global_gtt(struct drm_device *dev, - unsigned long start, - unsigned long mappable_end, - unsigned long end) +int i915_gem_setup_global_gtt(struct drm_device *dev, + unsigned long start, + unsigned long mappable_end, + unsigned long end) { /* Let GEM Manage all of the aperture. * @@ -1706,6 +1669,7 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, struct drm_mm_node *entry; struct drm_i915_gem_object *obj; unsigned long hole_start, hole_end; + int ret; BUG_ON(mappable_end > end); @@ -1717,14 +1681,16 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, /* Mark any preallocated objects as occupied */ list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) { struct i915_vma *vma = i915_gem_obj_to_vma(obj, ggtt_vm); - int ret; + DRM_DEBUG_KMS("reserving preallocated space: %lx + %zx\n", i915_gem_obj_ggtt_offset(obj), obj->base.size); WARN_ON(i915_gem_obj_ggtt_bound(obj)); ret = drm_mm_reserve_node(&ggtt_vm->mm, &vma->node); - if (ret) - DRM_DEBUG_KMS("Reservation failed\n"); + if (ret) { + DRM_DEBUG_KMS("Reservation failed: %i\n", ret); + return ret; + } obj->has_global_gtt_mapping = 1; } @@ -1741,6 +1707,22 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, /* And finally clear the reserved guard page */ ggtt_vm->clear_range(ggtt_vm, end - PAGE_SIZE, PAGE_SIZE, true); + + if (USES_PPGTT(dev) && !USES_FULL_PPGTT(dev)) { + struct i915_hw_ppgtt *ppgtt; + + ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL); + if (!ppgtt) + return -ENOMEM; + + ret = __hw_ppgtt_init(dev, ppgtt); + if (ret != 0) + return ret; + + dev_priv->mm.aliasing_ppgtt = ppgtt; + } + + return 0; } void i915_gem_init_global_gtt(struct drm_device *dev) @@ -1754,6 +1736,25 @@ void i915_gem_init_global_gtt(struct drm_device *dev) i915_gem_setup_global_gtt(dev, 0, mappable_size, gtt_size); } +void i915_global_gtt_cleanup(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct i915_address_space *vm = &dev_priv->gtt.base; + + if (dev_priv->mm.aliasing_ppgtt) { + struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; + + ppgtt->base.cleanup(&ppgtt->base); + } + + if (drm_mm_initialized(&vm->mm)) { + drm_mm_takedown(&vm->mm); + list_del(&vm->global_link); + } + + vm->cleanup(vm); +} + static int setup_scratch_page(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -2022,10 +2023,6 @@ static void gen6_gmch_remove(struct i915_address_space *vm) struct i915_gtt *gtt = container_of(vm, struct i915_gtt, base); - if (drm_mm_initialized(&vm->mm)) { - drm_mm_takedown(&vm->mm); - list_del(&vm->global_link); - } iounmap(gtt->gsm); teardown_scratch_page(vm->dev); } @@ -2058,10 +2055,6 @@ static int i915_gmch_probe(struct drm_device *dev, static void i915_gmch_remove(struct i915_address_space *vm) { - if (drm_mm_initialized(&vm->mm)) { - drm_mm_takedown(&vm->mm); - list_del(&vm->global_link); - } intel_gmch_remove(); } @@ -2160,8 +2153,10 @@ static struct i915_vma *__i915_gem_vma_create(struct drm_i915_gem_object *obj, /* Keep GGTT vmas first to make debug easier */ if (i915_is_ggtt(vm)) list_add(&vma->vma_link, &obj->vma_list); - else + else { list_add_tail(&vma->vma_link, &obj->vma_list); + i915_ppgtt_get(i915_vm_to_ppgtt(vm)); + } return vma; } diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h index 8d6f7c18c40413bf46288bec1144d26b5fc652dc..d5c14af51e995d5c19a6d37b950dc2691a44ffb2 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.h +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h @@ -34,6 +34,8 @@ #ifndef __I915_GEM_GTT_H__ #define __I915_GEM_GTT_H__ +struct drm_i915_file_private; + typedef uint32_t gen6_gtt_pte_t; typedef uint64_t gen8_gtt_pte_t; typedef gen8_gtt_pte_t gen8_ppgtt_pde_t; @@ -258,22 +260,36 @@ struct i915_hw_ppgtt { dma_addr_t *gen8_pt_dma_addr[4]; }; - struct intel_context *ctx; + struct drm_i915_file_private *file_priv; int (*enable)(struct i915_hw_ppgtt *ppgtt); int (*switch_mm)(struct i915_hw_ppgtt *ppgtt, - struct intel_engine_cs *ring, - bool synchronous); + struct intel_engine_cs *ring); void (*debug_dump)(struct i915_hw_ppgtt *ppgtt, struct seq_file *m); }; int i915_gem_gtt_init(struct drm_device *dev); void i915_gem_init_global_gtt(struct drm_device *dev); -void i915_gem_setup_global_gtt(struct drm_device *dev, unsigned long start, - unsigned long mappable_end, unsigned long end); - -bool intel_enable_ppgtt(struct drm_device *dev, bool full); -int i915_gem_init_ppgtt(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt); +int i915_gem_setup_global_gtt(struct drm_device *dev, unsigned long start, + unsigned long mappable_end, unsigned long end); +void i915_global_gtt_cleanup(struct drm_device *dev); + + +int i915_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt); +int i915_ppgtt_init_hw(struct drm_device *dev); +void i915_ppgtt_release(struct kref *kref); +struct i915_hw_ppgtt *i915_ppgtt_create(struct drm_device *dev, + struct drm_i915_file_private *fpriv); +static inline void i915_ppgtt_get(struct i915_hw_ppgtt *ppgtt) +{ + if (ppgtt) + kref_get(&ppgtt->ref); +} +static inline void i915_ppgtt_put(struct i915_hw_ppgtt *ppgtt) +{ + if (ppgtt) + kref_put(&ppgtt->ref, i915_ppgtt_release); +} void i915_check_and_clear_faults(struct drm_device *dev); void i915_gem_suspend_gtt_mappings(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem_render_state.c b/drivers/gpu/drm/i915/i915_gem_render_state.c index e60be3f552a6b1aed14770bef853a1eab2ddf5aa..a9a62d75aa577daa015543c7955c25cd5bf5b42a 100644 --- a/drivers/gpu/drm/i915/i915_gem_render_state.c +++ b/drivers/gpu/drm/i915/i915_gem_render_state.c @@ -28,13 +28,6 @@ #include "i915_drv.h" #include "intel_renderstate.h" -struct render_state { - const struct intel_renderstate_rodata *rodata; - struct drm_i915_gem_object *obj; - u64 ggtt_offset; - int gen; -}; - static const struct intel_renderstate_rodata * render_state_get_rodata(struct drm_device *dev, const int gen) { @@ -127,30 +120,47 @@ static int render_state_setup(struct render_state *so) return 0; } -static void render_state_fini(struct render_state *so) +void i915_gem_render_state_fini(struct render_state *so) { i915_gem_object_ggtt_unpin(so->obj); drm_gem_object_unreference(&so->obj->base); } -int i915_gem_render_state_init(struct intel_engine_cs *ring) +int i915_gem_render_state_prepare(struct intel_engine_cs *ring, + struct render_state *so) { - struct render_state so; int ret; if (WARN_ON(ring->id != RCS)) return -ENOENT; - ret = render_state_init(&so, ring->dev); + ret = render_state_init(so, ring->dev); if (ret) return ret; - if (so.rodata == NULL) + if (so->rodata == NULL) return 0; - ret = render_state_setup(&so); + ret = render_state_setup(so); + if (ret) { + i915_gem_render_state_fini(so); + return ret; + } + + return 0; +} + +int i915_gem_render_state_init(struct intel_engine_cs *ring) +{ + struct render_state so; + int ret; + + ret = i915_gem_render_state_prepare(ring, &so); if (ret) - goto out; + return ret; + + if (so.rodata == NULL) + return 0; ret = ring->dispatch_execbuffer(ring, so.ggtt_offset, @@ -164,6 +174,6 @@ int i915_gem_render_state_init(struct intel_engine_cs *ring) ret = __i915_add_request(ring, NULL, so.obj, NULL); /* __i915_add_request moves object to inactive if it fails */ out: - render_state_fini(&so); + i915_gem_render_state_fini(&so); return ret; } diff --git a/drivers/gpu/drm/i915/i915_gem_render_state.h b/drivers/gpu/drm/i915/i915_gem_render_state.h new file mode 100644 index 0000000000000000000000000000000000000000..c44961ed3fad4f6308af6594177e9b5f9a70f0cf --- /dev/null +++ b/drivers/gpu/drm/i915/i915_gem_render_state.h @@ -0,0 +1,47 @@ +/* + * Copyright © 2014 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef _I915_GEM_RENDER_STATE_H_ +#define _I915_GEM_RENDER_STATE_H_ + +#include + +struct intel_renderstate_rodata { + const u32 *reloc; + const u32 *batch; + const u32 batch_items; +}; + +struct render_state { + const struct intel_renderstate_rodata *rodata; + struct drm_i915_gem_object *obj; + u64 ggtt_offset; + int gen; +}; + +int i915_gem_render_state_init(struct intel_engine_cs *ring); +void i915_gem_render_state_fini(struct render_state *so); +int i915_gem_render_state_prepare(struct intel_engine_cs *ring, + struct render_state *so); + +#endif /* _I915_GEM_RENDER_STATE_H_ */ diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 21c025a209c079a88095fa8bd0de6c6d2926aa66..85fda6b803e4d33b8fbb405127f1a3d702478a8e 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -289,6 +289,7 @@ void i915_gem_cleanup_stolen(struct drm_device *dev) int i915_gem_init_stolen(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + u32 tmp; int bios_reserved = 0; #ifdef CONFIG_INTEL_IOMMU @@ -308,8 +309,16 @@ int i915_gem_init_stolen(struct drm_device *dev) DRM_DEBUG_KMS("found %zd bytes of stolen memory at %08lx\n", dev_priv->gtt.stolen_size, dev_priv->mm.stolen_base); - if (IS_VALLEYVIEW(dev)) - bios_reserved = 1024*1024; /* top 1M on VLV/BYT */ + if (INTEL_INFO(dev)->gen >= 8) { + tmp = I915_READ(GEN7_BIOS_RESERVED); + tmp >>= GEN8_BIOS_RESERVED_SHIFT; + tmp &= GEN8_BIOS_RESERVED_MASK; + bios_reserved = (1024*1024) << tmp; + } else if (IS_GEN7(dev)) { + tmp = I915_READ(GEN7_BIOS_RESERVED); + bios_reserved = tmp & GEN7_BIOS_RESERVED_256K ? + 256*1024 : 1024*1024; + } if (WARN_ON(bios_reserved > dev_priv->gtt.stolen_size)) return 0; diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index cb150e8b433658feaa3ecf6f4d817bc5b495a953..2cefb597df6dc92d446557442073d5c998843a30 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -91,7 +91,14 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev) uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN; uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN; - if (IS_VALLEYVIEW(dev)) { + if (INTEL_INFO(dev)->gen >= 8 || IS_VALLEYVIEW(dev)) { + /* + * On BDW+, swizzling is not used. We leave the CPU memory + * controller in charge of optimizing memory accesses without + * the extra address manipulation GPU side. + * + * VLV and CHV don't have GPU swizzling. + */ swizzle_x = I915_BIT_6_SWIZZLE_NONE; swizzle_y = I915_BIT_6_SWIZZLE_NONE; } else if (INTEL_INFO(dev)->gen >= 6) { @@ -376,7 +383,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, if (ret == 0) { obj->fence_dirty = - obj->fenced_gpu_access || + obj->last_fenced_seqno || obj->fence_reg != I915_FENCE_REG_NONE; obj->tiling_mode = args->tiling_mode; diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c index d384139973790e09d2da3583861c7224b1596f44..d182058383a9ef7264339101265e511ac8fa0609 100644 --- a/drivers/gpu/drm/i915/i915_gem_userptr.c +++ b/drivers/gpu/drm/i915/i915_gem_userptr.c @@ -293,15 +293,23 @@ i915_gem_userptr_release__mmu_notifier(struct drm_i915_gem_object *obj) static struct i915_mmu_notifier * i915_mmu_notifier_find(struct i915_mm_struct *mm) { - if (mm->mn == NULL) { - down_write(&mm->mm->mmap_sem); - mutex_lock(&to_i915(mm->dev)->mm_lock); - if (mm->mn == NULL) - mm->mn = i915_mmu_notifier_create(mm->mm); - mutex_unlock(&to_i915(mm->dev)->mm_lock); - up_write(&mm->mm->mmap_sem); + struct i915_mmu_notifier *mn = mm->mn; + + mn = mm->mn; + if (mn) + return mn; + + down_write(&mm->mm->mmap_sem); + mutex_lock(&to_i915(mm->dev)->mm_lock); + if ((mn = mm->mn) == NULL) { + mn = i915_mmu_notifier_create(mm->mm); + if (!IS_ERR(mn)) + mm->mn = mn; } - return mm->mn; + mutex_unlock(&to_i915(mm->dev)->mm_lock); + up_write(&mm->mm->mmap_sem); + + return mn; } static int @@ -681,16 +689,15 @@ i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj) static void i915_gem_userptr_put_pages(struct drm_i915_gem_object *obj) { - struct scatterlist *sg; - int i; + struct sg_page_iter sg_iter; BUG_ON(obj->userptr.work != NULL); if (obj->madv != I915_MADV_WILLNEED) obj->dirty = 0; - for_each_sg(obj->pages->sgl, sg, obj->pages->nents, i) { - struct page *page = sg_page(sg); + for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, 0) { + struct page *page = sg_page_iter_page(&sg_iter); if (obj->dirty) set_page_dirty(page); diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index eab41f9390f8c3bd1907c7625488c977a910ead3..2c87a797213f4e94305be2846bd9a7caa5765d51 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -192,10 +192,10 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m, struct drm_i915_error_buffer *err, int count) { - err_printf(m, "%s [%d]:\n", name, count); + err_printf(m, " %s [%d]:\n", name, count); while (count--) { - err_printf(m, " %08x %8u %02x %02x %x %x", + err_printf(m, " %08x %8u %02x %02x %x %x", err->gtt_offset, err->size, err->read_domains, @@ -208,7 +208,7 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m, err_puts(m, err->userptr ? " userptr" : ""); err_puts(m, err->ring != -1 ? " " : ""); err_puts(m, ring_str(err->ring)); - err_puts(m, i915_cache_level_str(err->cache_level)); + err_puts(m, i915_cache_level_str(m->i915, err->cache_level)); if (err->name) err_printf(m, " (name: %d)", err->name); @@ -393,15 +393,17 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, i915_ring_error_state(m, dev, &error->ring[i]); } - if (error->active_bo) + for (i = 0; i < error->vm_count; i++) { + err_printf(m, "vm[%d]\n", i); + print_error_buffers(m, "Active", - error->active_bo[0], - error->active_bo_count[0]); + error->active_bo[i], + error->active_bo_count[i]); - if (error->pinned_bo) print_error_buffers(m, "Pinned", - error->pinned_bo[0], - error->pinned_bo_count[0]); + error->pinned_bo[i], + error->pinned_bo_count[i]); + } for (i = 0; i < ARRAY_SIZE(error->ring); i++) { obj = error->ring[i].batchbuffer; @@ -492,9 +494,11 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, } int i915_error_state_buf_init(struct drm_i915_error_state_buf *ebuf, + struct drm_i915_private *i915, size_t count, loff_t pos) { memset(ebuf, 0, sizeof(*ebuf)); + ebuf->i915 = i915; /* We need to have enough room to store any i915_error_state printf * so that we can move it to start position. @@ -556,24 +560,54 @@ static void i915_error_state_free(struct kref *error_ref) } static struct drm_i915_error_object * -i915_error_object_create_sized(struct drm_i915_private *dev_priv, - struct drm_i915_gem_object *src, - struct i915_address_space *vm, - const int num_pages) +i915_error_object_create(struct drm_i915_private *dev_priv, + struct drm_i915_gem_object *src, + struct i915_address_space *vm) { struct drm_i915_error_object *dst; - int i; + int num_pages; + bool use_ggtt; + int i = 0; u32 reloc_offset; if (src == NULL || src->pages == NULL) return NULL; + num_pages = src->base.size >> PAGE_SHIFT; + dst = kmalloc(sizeof(*dst) + num_pages * sizeof(u32 *), GFP_ATOMIC); if (dst == NULL) return NULL; - reloc_offset = dst->gtt_offset = i915_gem_obj_offset(src, vm); - for (i = 0; i < num_pages; i++) { + if (i915_gem_obj_bound(src, vm)) + dst->gtt_offset = i915_gem_obj_offset(src, vm); + else + dst->gtt_offset = -1; + + reloc_offset = dst->gtt_offset; + use_ggtt = (src->cache_level == I915_CACHE_NONE && + i915_is_ggtt(vm) && + src->has_global_gtt_mapping && + reloc_offset + num_pages * PAGE_SIZE <= dev_priv->gtt.mappable_end); + + /* Cannot access stolen address directly, try to use the aperture */ + if (src->stolen) { + use_ggtt = true; + + if (!src->has_global_gtt_mapping) + goto unwind; + + reloc_offset = i915_gem_obj_ggtt_offset(src); + if (reloc_offset + num_pages * PAGE_SIZE > dev_priv->gtt.mappable_end) + goto unwind; + } + + /* Cannot access snooped pages through the aperture */ + if (use_ggtt && src->cache_level != I915_CACHE_NONE && !HAS_LLC(dev_priv->dev)) + goto unwind; + + dst->page_count = num_pages; + while (num_pages--) { unsigned long flags; void *d; @@ -582,10 +616,7 @@ i915_error_object_create_sized(struct drm_i915_private *dev_priv, goto unwind; local_irq_save(flags); - if (src->cache_level == I915_CACHE_NONE && - reloc_offset < dev_priv->gtt.mappable_end && - src->has_global_gtt_mapping && - i915_is_ggtt(vm)) { + if (use_ggtt) { void __iomem *s; /* Simply ignore tiling or any overlapping fence. @@ -597,14 +628,6 @@ i915_error_object_create_sized(struct drm_i915_private *dev_priv, reloc_offset); memcpy_fromio(d, s, PAGE_SIZE); io_mapping_unmap_atomic(s); - } else if (src->stolen) { - unsigned long offset; - - offset = dev_priv->mm.stolen_base; - offset += src->stolen->start; - offset += i << PAGE_SHIFT; - - memcpy_fromio(d, (void __iomem *) offset, PAGE_SIZE); } else { struct page *page; void *s; @@ -621,11 +644,9 @@ i915_error_object_create_sized(struct drm_i915_private *dev_priv, } local_irq_restore(flags); - dst->pages[i] = d; - + dst->pages[i++] = d; reloc_offset += PAGE_SIZE; } - dst->page_count = num_pages; return dst; @@ -635,22 +656,19 @@ i915_error_object_create_sized(struct drm_i915_private *dev_priv, kfree(dst); return NULL; } -#define i915_error_object_create(dev_priv, src, vm) \ - i915_error_object_create_sized((dev_priv), (src), (vm), \ - (src)->base.size>>PAGE_SHIFT) - #define i915_error_ggtt_object_create(dev_priv, src) \ - i915_error_object_create_sized((dev_priv), (src), &(dev_priv)->gtt.base, \ - (src)->base.size>>PAGE_SHIFT) + i915_error_object_create((dev_priv), (src), &(dev_priv)->gtt.base) static void capture_bo(struct drm_i915_error_buffer *err, - struct drm_i915_gem_object *obj) + struct i915_vma *vma) { + struct drm_i915_gem_object *obj = vma->obj; + err->size = obj->base.size; err->name = obj->base.name; err->rseqno = obj->last_read_seqno; err->wseqno = obj->last_write_seqno; - err->gtt_offset = i915_gem_obj_ggtt_offset(obj); + err->gtt_offset = vma->node.start; err->read_domains = obj->base.read_domains; err->write_domain = obj->base.write_domain; err->fence_reg = obj->fence_reg; @@ -674,7 +692,7 @@ static u32 capture_active_bo(struct drm_i915_error_buffer *err, int i = 0; list_for_each_entry(vma, head, mm_list) { - capture_bo(err++, vma->obj); + capture_bo(err++, vma); if (++i == count) break; } @@ -683,21 +701,27 @@ static u32 capture_active_bo(struct drm_i915_error_buffer *err, } static u32 capture_pinned_bo(struct drm_i915_error_buffer *err, - int count, struct list_head *head) + int count, struct list_head *head, + struct i915_address_space *vm) { struct drm_i915_gem_object *obj; - int i = 0; + struct drm_i915_error_buffer * const first = err; + struct drm_i915_error_buffer * const last = err + count; list_for_each_entry(obj, head, global_list) { - if (!i915_gem_obj_is_pinned(obj)) - continue; + struct i915_vma *vma; - capture_bo(err++, obj); - if (++i == count) + if (err == last) break; + + list_for_each_entry(vma, &obj->vma_list, vma_link) + if (vma->vm == vm && vma->pin_count > 0) { + capture_bo(err++, vma); + break; + } } - return i; + return err - first; } /* Generate a semi-unique error code. The code is not meant to have meaning, The @@ -890,9 +914,6 @@ static void i915_record_ring_state(struct drm_device *dev, ering->hws = I915_READ(mmio); } - ering->cpu_ring_head = ring->buffer->head; - ering->cpu_ring_tail = ring->buffer->tail; - ering->hangcheck_score = ring->hangcheck.score; ering->hangcheck_action = ring->hangcheck.action; @@ -955,6 +976,7 @@ static void i915_gem_record_rings(struct drm_device *dev, for (i = 0; i < I915_NUM_RINGS; i++) { struct intel_engine_cs *ring = &dev_priv->ring[i]; + struct intel_ringbuffer *rbuf; error->ring[i].pid = -1; @@ -967,6 +989,12 @@ static void i915_gem_record_rings(struct drm_device *dev, request = i915_gem_find_active_request(ring); if (request) { + struct i915_address_space *vm; + + vm = request->ctx && request->ctx->ppgtt ? + &request->ctx->ppgtt->base : + &dev_priv->gtt.base; + /* We need to copy these to an anonymous buffer * as the simplest method to avoid being overwritten * by userspace. @@ -974,12 +1002,9 @@ static void i915_gem_record_rings(struct drm_device *dev, error->ring[i].batchbuffer = i915_error_object_create(dev_priv, request->batch_obj, - request->ctx ? - request->ctx->vm : - &dev_priv->gtt.base); + vm); - if (HAS_BROKEN_CS_TLB(dev_priv->dev) && - ring->scratch.obj) + if (HAS_BROKEN_CS_TLB(dev_priv->dev)) error->ring[i].wa_batchbuffer = i915_error_ggtt_object_create(dev_priv, ring->scratch.obj); @@ -998,12 +1023,27 @@ static void i915_gem_record_rings(struct drm_device *dev, } } + if (i915.enable_execlists) { + /* TODO: This is only a small fix to keep basic error + * capture working, but we need to add more information + * for it to be useful (e.g. dump the context being + * executed). + */ + if (request) + rbuf = request->ctx->engine[ring->id].ringbuf; + else + rbuf = ring->default_context->engine[ring->id].ringbuf; + } else + rbuf = ring->buffer; + + error->ring[i].cpu_ring_head = rbuf->head; + error->ring[i].cpu_ring_tail = rbuf->tail; + error->ring[i].ringbuffer = - i915_error_ggtt_object_create(dev_priv, ring->buffer->obj); + i915_error_ggtt_object_create(dev_priv, rbuf->obj); - if (ring->status_page.obj) - error->ring[i].hws_page = - i915_error_ggtt_object_create(dev_priv, ring->status_page.obj); + error->ring[i].hws_page = + i915_error_ggtt_object_create(dev_priv, ring->status_page.obj); i915_gem_record_active_context(ring, error, &error->ring[i]); @@ -1049,9 +1089,14 @@ static void i915_gem_capture_vm(struct drm_i915_private *dev_priv, list_for_each_entry(vma, &vm->active_list, mm_list) i++; error->active_bo_count[ndx] = i; - list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) - if (i915_gem_obj_is_pinned(obj)) - i++; + + list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) { + list_for_each_entry(vma, &obj->vma_list, vma_link) + if (vma->vm == vm && vma->pin_count > 0) { + i++; + break; + } + } error->pinned_bo_count[ndx] = i - error->active_bo_count[ndx]; if (i) { @@ -1070,7 +1115,7 @@ static void i915_gem_capture_vm(struct drm_i915_private *dev_priv, error->pinned_bo_count[ndx] = capture_pinned_bo(pinned_bo, error->pinned_bo_count[ndx], - &dev_priv->mm.bound_list); + &dev_priv->mm.bound_list, vm); error->active_bo[ndx] = active_bo; error->pinned_bo[ndx] = pinned_bo; } @@ -1091,8 +1136,25 @@ static void i915_gem_capture_buffers(struct drm_i915_private *dev_priv, error->pinned_bo_count = kcalloc(cnt, sizeof(*error->pinned_bo_count), GFP_ATOMIC); - list_for_each_entry(vm, &dev_priv->vm_list, global_link) - i915_gem_capture_vm(dev_priv, error, vm, i++); + if (error->active_bo == NULL || + error->pinned_bo == NULL || + error->active_bo_count == NULL || + error->pinned_bo_count == NULL) { + kfree(error->active_bo); + kfree(error->active_bo_count); + kfree(error->pinned_bo); + kfree(error->pinned_bo_count); + + error->active_bo = NULL; + error->active_bo_count = NULL; + error->pinned_bo = NULL; + error->pinned_bo_count = NULL; + } else { + list_for_each_entry(vm, &dev_priv->vm_list, global_link) + i915_gem_capture_vm(dev_priv, error, vm, i++); + + error->vm_count = cnt; + } } /* Capture all registers which don't fit into another category. */ @@ -1295,11 +1357,11 @@ void i915_destroy_error_state(struct drm_device *dev) kref_put(&error->ref, i915_error_state_free); } -const char *i915_cache_level_str(int type) +const char *i915_cache_level_str(struct drm_i915_private *i915, int type) { switch (type) { case I915_CACHE_NONE: return " uncached"; - case I915_CACHE_LLC: return " snooped or LLC"; + case I915_CACHE_LLC: return HAS_LLC(i915) ? " LLC" : " snooped"; case I915_CACHE_L3_LLC: return " L3+LLC"; case I915_CACHE_WT: return " WT"; default: return ""; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 0050ee9470f196748621b96ffd001cd3e4cd85d8..3201986bf25ebaf6a5b2a31089bd2e2b1871e09d 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -151,7 +151,7 @@ ironlake_disable_display_irq(struct drm_i915_private *dev_priv, u32 mask) { assert_spin_locked(&dev_priv->irq_lock); - if (!intel_irqs_enabled(dev_priv)) + if (WARN_ON(!intel_irqs_enabled(dev_priv))) return; if ((dev_priv->irq_mask & mask) != mask) { @@ -238,7 +238,7 @@ static bool ivb_can_enable_err_int(struct drm_device *dev) assert_spin_locked(&dev_priv->irq_lock); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); if (crtc->cpu_fifo_underrun_disabled) @@ -296,7 +296,7 @@ static bool cpt_can_enable_serr_int(struct drm_device *dev) assert_spin_locked(&dev_priv->irq_lock); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); if (crtc->pch_fifo_underrun_disabled) @@ -497,7 +497,7 @@ static bool __intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev, old = !intel_crtc->cpu_fifo_underrun_disabled; intel_crtc->cpu_fifo_underrun_disabled = !enable; - if (INTEL_INFO(dev)->gen < 5 || IS_VALLEYVIEW(dev)) + if (HAS_GMCH_DISPLAY(dev)) i9xx_set_fifo_underrun_reporting(dev, pipe, enable, old); else if (IS_GEN5(dev) || IS_GEN6(dev)) ironlake_set_fifo_underrun_reporting(dev, pipe, enable); @@ -1020,7 +1020,7 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, /* In vblank? */ if (in_vbl) - ret |= DRM_SCANOUTPOS_INVBL; + ret |= DRM_SCANOUTPOS_IN_VBLANK; return ret; } @@ -1322,10 +1322,10 @@ static u32 vlv_c0_residency(struct drm_i915_private *dev_priv, * @dev_priv: DRM device private * */ -static u32 vlv_calc_delay_from_C0_counters(struct drm_i915_private *dev_priv) +static int vlv_calc_delay_from_C0_counters(struct drm_i915_private *dev_priv) { u32 residency_C0_up = 0, residency_C0_down = 0; - u8 new_delay, adj; + int new_delay, adj; dev_priv->rps.ei_interrupt_count++; @@ -1627,6 +1627,7 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev, struct drm_i915_private *dev_priv, u32 master_ctl) { + struct intel_engine_cs *ring; u32 rcs, bcs, vcs; uint32_t tmp = 0; irqreturn_t ret = IRQ_NONE; @@ -1636,12 +1637,20 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev, if (tmp) { I915_WRITE(GEN8_GT_IIR(0), tmp); ret = IRQ_HANDLED; + rcs = tmp >> GEN8_RCS_IRQ_SHIFT; - bcs = tmp >> GEN8_BCS_IRQ_SHIFT; + ring = &dev_priv->ring[RCS]; if (rcs & GT_RENDER_USER_INTERRUPT) - notify_ring(dev, &dev_priv->ring[RCS]); + notify_ring(dev, ring); + if (rcs & GT_CONTEXT_SWITCH_INTERRUPT) + intel_execlists_handle_ctx_events(ring); + + bcs = tmp >> GEN8_BCS_IRQ_SHIFT; + ring = &dev_priv->ring[BCS]; if (bcs & GT_RENDER_USER_INTERRUPT) - notify_ring(dev, &dev_priv->ring[BCS]); + notify_ring(dev, ring); + if (bcs & GT_CONTEXT_SWITCH_INTERRUPT) + intel_execlists_handle_ctx_events(ring); } else DRM_ERROR("The master control interrupt lied (GT0)!\n"); } @@ -1651,12 +1660,20 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev, if (tmp) { I915_WRITE(GEN8_GT_IIR(1), tmp); ret = IRQ_HANDLED; + vcs = tmp >> GEN8_VCS1_IRQ_SHIFT; + ring = &dev_priv->ring[VCS]; if (vcs & GT_RENDER_USER_INTERRUPT) - notify_ring(dev, &dev_priv->ring[VCS]); + notify_ring(dev, ring); + if (vcs & GT_CONTEXT_SWITCH_INTERRUPT) + intel_execlists_handle_ctx_events(ring); + vcs = tmp >> GEN8_VCS2_IRQ_SHIFT; + ring = &dev_priv->ring[VCS2]; if (vcs & GT_RENDER_USER_INTERRUPT) - notify_ring(dev, &dev_priv->ring[VCS2]); + notify_ring(dev, ring); + if (vcs & GT_CONTEXT_SWITCH_INTERRUPT) + intel_execlists_handle_ctx_events(ring); } else DRM_ERROR("The master control interrupt lied (GT1)!\n"); } @@ -1677,9 +1694,13 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev, if (tmp) { I915_WRITE(GEN8_GT_IIR(3), tmp); ret = IRQ_HANDLED; + vcs = tmp >> GEN8_VECS_IRQ_SHIFT; + ring = &dev_priv->ring[VECS]; if (vcs & GT_RENDER_USER_INTERRUPT) - notify_ring(dev, &dev_priv->ring[VECS]); + notify_ring(dev, ring); + if (vcs & GT_CONTEXT_SWITCH_INTERRUPT) + intel_execlists_handle_ctx_events(ring); } else DRM_ERROR("The master control interrupt lied (GT3)!\n"); } @@ -1772,7 +1793,9 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev, long_hpd = (dig_hotplug_reg >> dig_shift) & PORTB_HOTPLUG_LONG_DETECT; } - DRM_DEBUG_DRIVER("digital hpd port %d %d\n", port, long_hpd); + DRM_DEBUG_DRIVER("digital hpd port %c - %s\n", + port_name(port), + long_hpd ? "long" : "short"); /* for long HPD pulses we want to have the digital queue happen, but we still want HPD storm detection to function. */ if (long_hpd) { @@ -1984,14 +2007,9 @@ static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir) static bool intel_pipe_handle_vblank(struct drm_device *dev, enum pipe pipe) { - struct intel_crtc *crtc; - if (!drm_handle_vblank(dev, pipe)) return false; - crtc = to_intel_crtc(intel_get_crtc_for_pipe(dev, pipe)); - wake_up(&crtc->vbl_wait); - return true; } @@ -2002,7 +2020,7 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir) int pipe; spin_lock(&dev_priv->irq_lock); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { int reg; u32 mask, iir_bit = 0; @@ -2047,9 +2065,10 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir) } spin_unlock(&dev_priv->irq_lock); - for_each_pipe(pipe) { - if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS) - intel_pipe_handle_vblank(dev, pipe); + for_each_pipe(dev_priv, pipe) { + if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS && + intel_pipe_handle_vblank(dev, pipe)) + intel_check_page_flip(dev, pipe); if (pipe_stats[pipe] & PLANE_FLIP_DONE_INT_STATUS_VLV) { intel_prepare_page_flip(dev, pipe); @@ -2216,7 +2235,7 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) DRM_ERROR("PCH poison interrupt\n"); if (pch_iir & SDE_FDI_MASK) - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) DRM_DEBUG_DRIVER(" pipe %c FDI IIR: 0x%08x\n", pipe_name(pipe), I915_READ(FDI_RX_IIR(pipe))); @@ -2247,7 +2266,7 @@ static void ivb_err_int_handler(struct drm_device *dev) if (err_int & ERR_INT_POISON) DRM_ERROR("Poison interrupt\n"); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { if (err_int & ERR_INT_FIFO_UNDERRUN(pipe)) { if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, false)) @@ -2324,7 +2343,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) DRM_DEBUG_DRIVER("Audio CP change interrupt\n"); if (pch_iir & SDE_FDI_MASK_CPT) - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) DRM_DEBUG_DRIVER(" pipe %c FDI IIR: 0x%08x\n", pipe_name(pipe), I915_READ(FDI_RX_IIR(pipe))); @@ -2347,9 +2366,10 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir) if (de_iir & DE_POISON) DRM_ERROR("Poison interrupt\n"); - for_each_pipe(pipe) { - if (de_iir & DE_PIPE_VBLANK(pipe)) - intel_pipe_handle_vblank(dev, pipe); + for_each_pipe(dev_priv, pipe) { + if (de_iir & DE_PIPE_VBLANK(pipe) && + intel_pipe_handle_vblank(dev, pipe)) + intel_check_page_flip(dev, pipe); if (de_iir & DE_PIPE_FIFO_UNDERRUN(pipe)) if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, false)) @@ -2397,9 +2417,10 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir) if (de_iir & DE_GSE_IVB) intel_opregion_asle_intr(dev); - for_each_pipe(pipe) { - if (de_iir & (DE_PIPE_VBLANK_IVB(pipe))) - intel_pipe_handle_vblank(dev, pipe); + for_each_pipe(dev_priv, pipe) { + if (de_iir & (DE_PIPE_VBLANK_IVB(pipe)) && + intel_pipe_handle_vblank(dev, pipe)) + intel_check_page_flip(dev, pipe); /* plane/pipes map 1:1 on ilk+ */ if (de_iir & DE_PLANE_FLIP_DONE_IVB(pipe)) { @@ -2544,7 +2565,7 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg) DRM_ERROR("The master control interrupt lied (DE PORT)!\n"); } - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { uint32_t pipe_iir; if (!(master_ctl & GEN8_DE_PIPE_IRQ(pipe))) @@ -2554,8 +2575,9 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg) if (pipe_iir) { ret = IRQ_HANDLED; I915_WRITE(GEN8_DE_PIPE_IIR(pipe), pipe_iir); - if (pipe_iir & GEN8_PIPE_VBLANK) - intel_pipe_handle_vblank(dev, pipe); + if (pipe_iir & GEN8_PIPE_VBLANK && + intel_pipe_handle_vblank(dev, pipe)) + intel_check_page_flip(dev, pipe); if (pipe_iir & GEN8_PIPE_PRIMARY_FLIP_DONE) { intel_prepare_page_flip(dev, pipe); @@ -2763,7 +2785,7 @@ static void i915_report_and_clear_eir(struct drm_device *dev) if (eir & I915_ERROR_MEMORY_REFRESH) { pr_err("memory refresh error:\n"); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) pr_err("pipe %c stat: 0x%08x\n", pipe_name(pipe), I915_READ(PIPESTAT(pipe))); /* pipestat has already been acked */ @@ -2860,52 +2882,6 @@ void i915_handle_error(struct drm_device *dev, bool wedged, schedule_work(&dev_priv->gpu_error.work); } -static void __always_unused i915_pageflip_stall_check(struct drm_device *dev, int pipe) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct drm_i915_gem_object *obj; - struct intel_unpin_work *work; - unsigned long flags; - bool stall_detected; - - /* Ignore early vblank irqs */ - if (intel_crtc == NULL) - return; - - spin_lock_irqsave(&dev->event_lock, flags); - work = intel_crtc->unpin_work; - - if (work == NULL || - atomic_read(&work->pending) >= INTEL_FLIP_COMPLETE || - !work->enable_stall_check) { - /* Either the pending flip IRQ arrived, or we're too early. Don't check */ - spin_unlock_irqrestore(&dev->event_lock, flags); - return; - } - - /* Potential stall - if we see that the flip has happened, assume a missed interrupt */ - obj = work->pending_flip_obj; - if (INTEL_INFO(dev)->gen >= 4) { - int dspsurf = DSPSURF(intel_crtc->plane); - stall_detected = I915_HI_DISPBASE(I915_READ(dspsurf)) == - i915_gem_obj_ggtt_offset(obj); - } else { - int dspaddr = DSPADDR(intel_crtc->plane); - stall_detected = I915_READ(dspaddr) == (i915_gem_obj_ggtt_offset(obj) + - crtc->y * crtc->primary->fb->pitches[0] + - crtc->x * crtc->primary->fb->bits_per_pixel/8); - } - - spin_unlock_irqrestore(&dev->event_lock, flags); - - if (stall_detected) { - DRM_DEBUG_DRIVER("Pageflip stall detected\n"); - intel_prepare_page_flip(dev, intel_crtc->plane); - } -} - /* Called from drm generic code, passed 'crtc' which * we use as a pipe index */ @@ -3441,7 +3417,7 @@ static void valleyview_irq_preinstall(struct drm_device *dev) I915_WRITE(PORT_HOTPLUG_EN, 0); I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0xffff); I915_WRITE(VLV_IIR, 0xffffffff); I915_WRITE(VLV_IMR, 0xffffffff); @@ -3467,7 +3443,7 @@ static void gen8_irq_reset(struct drm_device *dev) gen8_gt_irq_reset(dev_priv); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) if (intel_display_power_enabled(dev_priv, POWER_DOMAIN_PIPE(pipe))) GEN8_IRQ_RESET_NDX(DE_PIPE, pipe); @@ -3510,7 +3486,7 @@ static void cherryview_irq_preinstall(struct drm_device *dev) I915_WRITE(PORT_HOTPLUG_EN, 0); I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0xffff); I915_WRITE(VLV_IMR, 0xffffffff); @@ -3522,18 +3498,17 @@ static void cherryview_irq_preinstall(struct drm_device *dev) static void ibx_hpd_irq_setup(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_mode_config *mode_config = &dev->mode_config; struct intel_encoder *intel_encoder; u32 hotplug_irqs, hotplug, enabled_irqs = 0; if (HAS_PCH_IBX(dev)) { hotplug_irqs = SDE_HOTPLUG_MASK; - list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head) + for_each_intel_encoder(dev, intel_encoder) if (dev_priv->hpd_stats[intel_encoder->hpd_pin].hpd_mark == HPD_ENABLED) enabled_irqs |= hpd_ibx[intel_encoder->hpd_pin]; } else { hotplug_irqs = SDE_HOTPLUG_MASK_CPT; - list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head) + for_each_intel_encoder(dev, intel_encoder) if (dev_priv->hpd_stats[intel_encoder->hpd_pin].hpd_mark == HPD_ENABLED) enabled_irqs |= hpd_cpt[intel_encoder->hpd_pin]; } @@ -3782,28 +3757,31 @@ static int valleyview_irq_postinstall(struct drm_device *dev) static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv) { - int i; - /* These are interrupts we'll toggle with the ring mask register */ uint32_t gt_interrupts[] = { GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT | + GT_CONTEXT_SWITCH_INTERRUPT << GEN8_RCS_IRQ_SHIFT | GT_RENDER_L3_PARITY_ERROR_INTERRUPT | - GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT, + GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT | + GT_CONTEXT_SWITCH_INTERRUPT << GEN8_BCS_IRQ_SHIFT, GT_RENDER_USER_INTERRUPT << GEN8_VCS1_IRQ_SHIFT | - GT_RENDER_USER_INTERRUPT << GEN8_VCS2_IRQ_SHIFT, + GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VCS1_IRQ_SHIFT | + GT_RENDER_USER_INTERRUPT << GEN8_VCS2_IRQ_SHIFT | + GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VCS2_IRQ_SHIFT, 0, - GT_RENDER_USER_INTERRUPT << GEN8_VECS_IRQ_SHIFT + GT_RENDER_USER_INTERRUPT << GEN8_VECS_IRQ_SHIFT | + GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VECS_IRQ_SHIFT }; - for (i = 0; i < ARRAY_SIZE(gt_interrupts); i++) - GEN8_IRQ_INIT_NDX(GT, i, ~gt_interrupts[i], gt_interrupts[i]); - dev_priv->pm_irq_mask = 0xffffffff; + GEN8_IRQ_INIT_NDX(GT, 0, ~gt_interrupts[0], gt_interrupts[0]); + GEN8_IRQ_INIT_NDX(GT, 1, ~gt_interrupts[1], gt_interrupts[1]); + GEN8_IRQ_INIT_NDX(GT, 2, dev_priv->pm_irq_mask, dev_priv->pm_rps_events); + GEN8_IRQ_INIT_NDX(GT, 3, ~gt_interrupts[3], gt_interrupts[3]); } static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) { - struct drm_device *dev = dev_priv->dev; uint32_t de_pipe_masked = GEN8_PIPE_PRIMARY_FLIP_DONE | GEN8_PIPE_CDCLK_CRC_DONE | GEN8_DE_PIPE_IRQ_FAULT_ERRORS; @@ -3814,7 +3792,7 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) dev_priv->de_irq_mask[PIPE_B] = ~de_pipe_masked; dev_priv->de_irq_mask[PIPE_C] = ~de_pipe_masked; - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) if (intel_display_power_enabled(dev_priv, POWER_DOMAIN_PIPE(pipe))) GEN8_IRQ_INIT_NDX(DE_PIPE, pipe, @@ -3859,12 +3837,12 @@ static int cherryview_irq_postinstall(struct drm_device *dev) */ dev_priv->irq_mask = ~enable_mask; - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0xffff); spin_lock_irqsave(&dev_priv->irq_lock, irqflags); i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) i915_enable_pipestat(dev_priv, pipe, pipestat_enable); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); @@ -3901,7 +3879,7 @@ static void valleyview_irq_uninstall(struct drm_device *dev) I915_WRITE(VLV_MASTER_IER, 0); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0xffff); I915_WRITE(HWSTAM, 0xffffffff); @@ -3963,7 +3941,7 @@ do { \ I915_WRITE(PORT_HOTPLUG_EN, 0); I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0xffff); I915_WRITE(VLV_IMR, 0xffffffff); @@ -3987,7 +3965,7 @@ static void i8xx_irq_preinstall(struct drm_device * dev) struct drm_i915_private *dev_priv = dev->dev_private; int pipe; - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0); I915_WRITE16(IMR, 0xffff); I915_WRITE16(IER, 0x0); @@ -4041,7 +4019,7 @@ static bool i8xx_handle_vblank(struct drm_device *dev, return false; if ((iir & flip_pending) == 0) - return false; + goto check_page_flip; intel_prepare_page_flip(dev, plane); @@ -4052,11 +4030,14 @@ static bool i8xx_handle_vblank(struct drm_device *dev, * an interrupt per se, we watch for the change at vblank. */ if (I915_READ16(ISR) & flip_pending) - return false; + goto check_page_flip; intel_finish_page_flip(dev, pipe); - return true; + +check_page_flip: + intel_check_page_flip(dev, pipe); + return false; } static irqreturn_t i8xx_irq_handler(int irq, void *arg) @@ -4087,7 +4068,7 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg) "Command parser error, iir 0x%08x", iir); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { int reg = PIPESTAT(pipe); pipe_stats[pipe] = I915_READ(reg); @@ -4107,7 +4088,7 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg) if (iir & I915_USER_INTERRUPT) notify_ring(dev, &dev_priv->ring[RCS]); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { int plane = pipe; if (HAS_FBC(dev)) plane = !plane; @@ -4135,7 +4116,7 @@ static void i8xx_irq_uninstall(struct drm_device * dev) struct drm_i915_private *dev_priv = dev->dev_private; int pipe; - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { /* Clear enable bits; then clear status bits */ I915_WRITE(PIPESTAT(pipe), 0); I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe))); @@ -4156,7 +4137,7 @@ static void i915_irq_preinstall(struct drm_device * dev) } I915_WRITE16(HWSTAM, 0xeffe); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0); I915_WRITE(IMR, 0xffffffff); I915_WRITE(IER, 0x0); @@ -4226,7 +4207,7 @@ static bool i915_handle_vblank(struct drm_device *dev, return false; if ((iir & flip_pending) == 0) - return false; + goto check_page_flip; intel_prepare_page_flip(dev, plane); @@ -4237,11 +4218,14 @@ static bool i915_handle_vblank(struct drm_device *dev, * an interrupt per se, we watch for the change at vblank. */ if (I915_READ(ISR) & flip_pending) - return false; + goto check_page_flip; intel_finish_page_flip(dev, pipe); - return true; + +check_page_flip: + intel_check_page_flip(dev, pipe); + return false; } static irqreturn_t i915_irq_handler(int irq, void *arg) @@ -4271,7 +4255,7 @@ static irqreturn_t i915_irq_handler(int irq, void *arg) "Command parser error, iir 0x%08x", iir); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { int reg = PIPESTAT(pipe); pipe_stats[pipe] = I915_READ(reg); @@ -4297,7 +4281,7 @@ static irqreturn_t i915_irq_handler(int irq, void *arg) if (iir & I915_USER_INTERRUPT) notify_ring(dev, &dev_priv->ring[RCS]); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { int plane = pipe; if (HAS_FBC(dev)) plane = !plane; @@ -4355,7 +4339,7 @@ static void i915_irq_uninstall(struct drm_device * dev) } I915_WRITE16(HWSTAM, 0xffff); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { /* Clear enable bits; then clear status bits */ I915_WRITE(PIPESTAT(pipe), 0); I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe))); @@ -4375,7 +4359,7 @@ static void i965_irq_preinstall(struct drm_device * dev) I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); I915_WRITE(HWSTAM, 0xeffe); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0); I915_WRITE(IMR, 0xffffffff); I915_WRITE(IER, 0x0); @@ -4444,7 +4428,6 @@ static int i965_irq_postinstall(struct drm_device *dev) static void i915_hpd_irq_setup(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_mode_config *mode_config = &dev->mode_config; struct intel_encoder *intel_encoder; u32 hotplug_en; @@ -4455,7 +4438,7 @@ static void i915_hpd_irq_setup(struct drm_device *dev) hotplug_en &= ~HOTPLUG_INT_EN_MASK; /* Note HDMI and DP share hotplug bits */ /* enable bits are the same for all generations */ - list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head) + for_each_intel_encoder(dev, intel_encoder) if (dev_priv->hpd_stats[intel_encoder->hpd_pin].hpd_mark == HPD_ENABLED) hotplug_en |= hpd_mask_i915[intel_encoder->hpd_pin]; /* Programming the CRT detection parameters tends @@ -4501,7 +4484,7 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) "Command parser error, iir 0x%08x", iir); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { int reg = PIPESTAT(pipe); pipe_stats[pipe] = I915_READ(reg); @@ -4532,7 +4515,7 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) if (iir & I915_BSD_USER_INTERRUPT) notify_ring(dev, &dev_priv->ring[VCS]); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS && i915_handle_vblank(dev, pipe, pipe, iir)) flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(pipe); @@ -4589,12 +4572,12 @@ static void i965_irq_uninstall(struct drm_device * dev) I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); I915_WRITE(HWSTAM, 0xffffffff); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), 0); I915_WRITE(IMR, 0xffffffff); I915_WRITE(IER, 0x0); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe)) & 0x8000ffff); I915_WRITE(IIR, I915_READ(IIR)); @@ -4652,8 +4635,8 @@ void intel_irq_init(struct drm_device *dev) INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work); /* Let's track the enabled rps events */ - if (IS_VALLEYVIEW(dev)) - /* WaGsvRC0ResidenncyMethod:VLV */ + if (IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev)) + /* WaGsvRC0ResidencyMethod:vlv */ dev_priv->pm_rps_events = GEN6_PM_RP_UP_EI_EXPIRED; else dev_priv->pm_rps_events = GEN6_PM_RPS_EVENTS; @@ -4680,6 +4663,14 @@ void intel_irq_init(struct drm_device *dev) dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ } + /* + * Opt out of the vblank disable timer on everything except gen2. + * Gen2 doesn't have a hardware frame counter and so depends on + * vblank interrupts to produce sane vblank seuquence numbers. + */ + if (!IS_GEN2(dev)) + dev->vblank_disable_immediate = true; + if (drm_core_check_feature(dev, DRIVER_MODESET)) { dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp; dev->driver->get_scanout_position = i915_get_crtc_scanoutpos; diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 9842fd2e742a40d0b83c51f2e9f2dd012e1bc304..c91cb2033cc5ec02c248b95271fac95469e1acde 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -35,6 +35,7 @@ struct i915_params i915 __read_mostly = { .vbt_sdvo_panel_type = -1, .enable_rc6 = -1, .enable_fbc = -1, + .enable_execlists = 0, .enable_hangcheck = true, .enable_ppgtt = -1, .enable_psr = 0, @@ -118,6 +119,11 @@ MODULE_PARM_DESC(enable_ppgtt, "Override PPGTT usage. " "(-1=auto [default], 0=disabled, 1=aliasing, 2=full)"); +module_param_named(enable_execlists, i915.enable_execlists, int, 0400); +MODULE_PARM_DESC(enable_execlists, + "Override execlists usage. " + "(-1=auto, 0=disabled [default], 1=enabled)"); + module_param_named(enable_psr, i915.enable_psr, int, 0600); MODULE_PARM_DESC(enable_psr, "Enable PSR (default: false)"); diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index f29b44c86a2f7354c29dd0ce3ca3a5cfc7ae723c..c01e5f31430ec420f469aacda6c520b39ccb158e 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -143,6 +143,14 @@ #define GAB_CTL 0x24000 #define GAB_CTL_CONT_AFTER_PAGEFAULT (1<<8) +#define GEN7_BIOS_RESERVED 0x1082C0 +#define GEN7_BIOS_RESERVED_1M (0 << 5) +#define GEN7_BIOS_RESERVED_256K (1 << 5) +#define GEN8_BIOS_RESERVED_SHIFT 7 +#define GEN7_BIOS_RESERVED_MASK 0x1 +#define GEN8_BIOS_RESERVED_MASK 0x3 + + /* VGA stuff */ #define VGA_ST01_MDA 0x3ba @@ -272,6 +280,7 @@ #define MI_SEMAPHORE_POLL (1<<15) #define MI_SEMAPHORE_SAD_GTE_SDD (1<<12) #define MI_STORE_DWORD_IMM MI_INSTR(0x20, 1) +#define MI_STORE_DWORD_IMM_GEN8 MI_INSTR(0x20, 2) #define MI_MEM_VIRTUAL (1 << 22) /* 965+ only */ #define MI_STORE_DWORD_INDEX MI_INSTR(0x21, 1) #define MI_STORE_DWORD_INDEX_SHIFT 2 @@ -282,6 +291,7 @@ * address/value pairs. Don't overdue it, though, x <= 2^4 must hold! */ #define MI_LOAD_REGISTER_IMM(x) MI_INSTR(0x22, 2*(x)-1) +#define MI_LRI_FORCE_POSTED (1<<12) #define MI_STORE_REGISTER_MEM(x) MI_INSTR(0x24, 2*(x)-1) #define MI_STORE_REGISTER_MEM_GEN8(x) MI_INSTR(0x24, 3*(x)-1) #define MI_SRM_LRM_GLOBAL_GTT (1<<22) @@ -501,10 +511,26 @@ #define BUNIT_REG_BISOC 0x11 #define PUNIT_REG_DSPFREQ 0x36 +#define DSPFREQSTAT_SHIFT_CHV 24 +#define DSPFREQSTAT_MASK_CHV (0x1f << DSPFREQSTAT_SHIFT_CHV) +#define DSPFREQGUAR_SHIFT_CHV 8 +#define DSPFREQGUAR_MASK_CHV (0x1f << DSPFREQGUAR_SHIFT_CHV) #define DSPFREQSTAT_SHIFT 30 #define DSPFREQSTAT_MASK (0x3 << DSPFREQSTAT_SHIFT) #define DSPFREQGUAR_SHIFT 14 #define DSPFREQGUAR_MASK (0x3 << DSPFREQGUAR_SHIFT) +#define _DP_SSC(val, pipe) ((val) << (2 * (pipe))) +#define DP_SSC_MASK(pipe) _DP_SSC(0x3, (pipe)) +#define DP_SSC_PWR_ON(pipe) _DP_SSC(0x0, (pipe)) +#define DP_SSC_CLK_GATE(pipe) _DP_SSC(0x1, (pipe)) +#define DP_SSC_RESET(pipe) _DP_SSC(0x2, (pipe)) +#define DP_SSC_PWR_GATE(pipe) _DP_SSC(0x3, (pipe)) +#define _DP_SSS(val, pipe) ((val) << (2 * (pipe) + 16)) +#define DP_SSS_MASK(pipe) _DP_SSS(0x3, (pipe)) +#define DP_SSS_PWR_ON(pipe) _DP_SSS(0x0, (pipe)) +#define DP_SSS_CLK_GATE(pipe) _DP_SSS(0x1, (pipe)) +#define DP_SSS_RESET(pipe) _DP_SSS(0x2, (pipe)) +#define DP_SSS_PWR_GATE(pipe) _DP_SSS(0x3, (pipe)) /* See the PUNIT HAS v0.8 for the below bits */ enum punit_power_well { @@ -518,6 +544,11 @@ enum punit_power_well { PUNIT_POWER_WELL_DPIO_TX_C_LANES_23 = 9, PUNIT_POWER_WELL_DPIO_RX0 = 10, PUNIT_POWER_WELL_DPIO_RX1 = 11, + PUNIT_POWER_WELL_DPIO_CMN_D = 12, + /* FIXME: guesswork below */ + PUNIT_POWER_WELL_DPIO_TX_D_LANES_01 = 13, + PUNIT_POWER_WELL_DPIO_TX_D_LANES_23 = 14, + PUNIT_POWER_WELL_DPIO_RX2 = 15, PUNIT_POWER_WELL_NUM, }; @@ -838,8 +869,8 @@ enum punit_power_well { #define _VLV_TX_DW2_CH0 0x8288 #define _VLV_TX_DW2_CH1 0x8488 -#define DPIO_SWING_MARGIN_SHIFT 16 -#define DPIO_SWING_MARGIN_MASK (0xff << DPIO_SWING_MARGIN_SHIFT) +#define DPIO_SWING_MARGIN000_SHIFT 16 +#define DPIO_SWING_MARGIN000_MASK (0xff << DPIO_SWING_MARGIN000_SHIFT) #define DPIO_UNIQ_TRANS_SCALE_SHIFT 8 #define VLV_TX_DW2(ch) _PORT(ch, _VLV_TX_DW2_CH0, _VLV_TX_DW2_CH1) @@ -847,12 +878,16 @@ enum punit_power_well { #define _VLV_TX_DW3_CH1 0x848c /* The following bit for CHV phy */ #define DPIO_TX_UNIQ_TRANS_SCALE_EN (1<<27) +#define DPIO_SWING_MARGIN101_SHIFT 16 +#define DPIO_SWING_MARGIN101_MASK (0xff << DPIO_SWING_MARGIN101_SHIFT) #define VLV_TX_DW3(ch) _PORT(ch, _VLV_TX_DW3_CH0, _VLV_TX_DW3_CH1) #define _VLV_TX_DW4_CH0 0x8290 #define _VLV_TX_DW4_CH1 0x8490 #define DPIO_SWING_DEEMPH9P5_SHIFT 24 #define DPIO_SWING_DEEMPH9P5_MASK (0xff << DPIO_SWING_DEEMPH9P5_SHIFT) +#define DPIO_SWING_DEEMPH6P0_SHIFT 16 +#define DPIO_SWING_DEEMPH6P0_MASK (0xff << DPIO_SWING_DEEMPH6P0_SHIFT) #define VLV_TX_DW4(ch) _PORT(ch, _VLV_TX_DW4_CH0, _VLV_TX_DW4_CH1) #define _VLV_TX3_DW4_CH0 0x690 @@ -1003,6 +1038,13 @@ enum punit_power_well { #define PGTBL_ADDRESS_LO_MASK 0xfffff000 /* bits [31:12] */ #define PGTBL_ADDRESS_HI_MASK 0x000000f0 /* bits [35:32] (gen4) */ #define PGTBL_ER 0x02024 +#define PRB0_BASE (0x2030-0x30) +#define PRB1_BASE (0x2040-0x30) /* 830,gen3 */ +#define PRB2_BASE (0x2050-0x30) /* gen3 */ +#define SRB0_BASE (0x2100-0x30) /* gen2 */ +#define SRB1_BASE (0x2110-0x30) /* gen2 */ +#define SRB2_BASE (0x2120-0x30) /* 830 */ +#define SRB3_BASE (0x2130-0x30) /* 830 */ #define RENDER_RING_BASE 0x02000 #define BSD_RING_BASE 0x04000 #define GEN6_BSD_RING_BASE 0x12000 @@ -1064,6 +1106,7 @@ enum punit_power_well { #define RING_ACTHD_UDW(base) ((base)+0x5c) #define RING_NOPID(base) ((base)+0x94) #define RING_IMR(base) ((base)+0xa8) +#define RING_HWSTAM(base) ((base)+0x98) #define RING_TIMESTAMP(base) ((base)+0x358) #define TAIL_ADDR 0x001FFFF8 #define HEAD_WRAP_COUNT 0xFFE00000 @@ -1248,6 +1291,10 @@ enum punit_power_well { #define INSTPM_TLB_INVALIDATE (1<<9) #define INSTPM_SYNC_FLUSH (1<<5) #define ACTHD 0x020c8 +#define MEM_MODE 0x020cc +#define MEM_DISPLAY_B_TRICKLE_FEED_DISABLE (1<<3) /* 830 only */ +#define MEM_DISPLAY_A_TRICKLE_FEED_DISABLE (1<<2) /* 830/845 only */ +#define MEM_DISPLAY_TRICKLE_FEED_DISABLE (1<<2) /* 85x only */ #define FW_BLC 0x020d8 #define FW_BLC2 0x020dc #define FW_BLC_SELF 0x020e0 /* 915+ only */ @@ -1380,6 +1427,7 @@ enum punit_power_well { #define GT_BSD_CS_ERROR_INTERRUPT (1 << 15) #define GT_BSD_USER_INTERRUPT (1 << 12) #define GT_RENDER_L3_PARITY_ERROR_INTERRUPT_S1 (1 << 11) /* hsw+; rsvd on snb, ivb, vlv */ +#define GT_CONTEXT_SWITCH_INTERRUPT (1 << 8) #define GT_RENDER_L3_PARITY_ERROR_INTERRUPT (1 << 5) /* !snb */ #define GT_RENDER_PIPECTL_NOTIFY_INTERRUPT (1 << 4) #define GT_RENDER_CS_MASTER_ERROR_INTERRUPT (1 << 3) @@ -1519,6 +1567,7 @@ enum punit_power_well { /* Framebuffer compression for Ironlake */ #define ILK_DPFC_CB_BASE 0x43200 #define ILK_DPFC_CONTROL 0x43208 +#define FBC_CTL_FALSE_COLOR (1<<10) /* The bit 28-8 is reserved */ #define DPFC_RESERVED (0x1FFFFF00) #define ILK_DPFC_RECOMP_CTL 0x4320c @@ -1675,12 +1724,9 @@ enum punit_power_well { #define DPIO_PHY_STATUS (VLV_DISPLAY_BASE + 0x6240) #define DPLL_PORTD_READY_MASK (0xf) #define DISPLAY_PHY_CONTROL (VLV_DISPLAY_BASE + 0x60100) -#define PHY_COM_LANE_RESET_DEASSERT(phy, val) \ - ((phy == DPIO_PHY0) ? (val | 1) : (val | 2)) -#define PHY_COM_LANE_RESET_ASSERT(phy, val) \ - ((phy == DPIO_PHY0) ? (val & ~1) : (val & ~2)) +#define PHY_COM_LANE_RESET_DEASSERT(phy) (1 << (phy)) #define DISPLAY_PHY_STATUS (VLV_DISPLAY_BASE + 0x60104) -#define PHY_POWERGOOD(phy) ((phy == DPIO_PHY0) ? (1<<31) : (1<<30)) +#define PHY_POWERGOOD(phy) (((phy) == DPIO_PHY0) ? (1<<31) : (1<<30)) /* * The i830 generation, in LVDS mode, defines P1 as the bit number set within @@ -2397,6 +2443,7 @@ enum punit_power_well { #define _PIPEASRC 0x6001c #define _BCLRPAT_A 0x60020 #define _VSYNCSHIFT_A 0x60028 +#define _PIPE_MULT_A 0x6002c /* Pipe B timing regs */ #define _HTOTAL_B 0x61000 @@ -2408,6 +2455,7 @@ enum punit_power_well { #define _PIPEBSRC 0x6101c #define _BCLRPAT_B 0x61020 #define _VSYNCSHIFT_B 0x61028 +#define _PIPE_MULT_B 0x6102c #define TRANSCODER_A_OFFSET 0x60000 #define TRANSCODER_B_OFFSET 0x61000 @@ -2428,6 +2476,7 @@ enum punit_power_well { #define BCLRPAT(trans) _TRANSCODER2(trans, _BCLRPAT_A) #define VSYNCSHIFT(trans) _TRANSCODER2(trans, _VSYNCSHIFT_A) #define PIPESRC(trans) _TRANSCODER2(trans, _PIPEASRC) +#define PIPE_MULT(trans) _TRANSCODER2(trans, _PIPE_MULT_A) /* HSW+ eDP PSR registers */ #define EDP_PSR_BASE(dev) (IS_HASWELL(dev) ? 0x64800 : 0x6f800) @@ -3476,6 +3525,8 @@ enum punit_power_well { #define DP_LINK_TRAIN_OFF (3 << 28) #define DP_LINK_TRAIN_MASK (3 << 28) #define DP_LINK_TRAIN_SHIFT 28 +#define DP_LINK_TRAIN_PAT_3_CHV (1 << 14) +#define DP_LINK_TRAIN_MASK_CHV ((3 << 28)|(1<<14)) /* CPT Link training mode */ #define DP_LINK_TRAIN_PAT_1_CPT (0 << 8) @@ -3732,7 +3783,6 @@ enum punit_power_well { #define PIPE_VSYNC_INTERRUPT_STATUS (1UL<<9) #define PIPE_DISPLAY_LINE_COMPARE_STATUS (1UL<<8) #define PIPE_DPST_EVENT_STATUS (1UL<<7) -#define PIPE_LEGACY_BLC_EVENT_STATUS (1UL<<6) #define PIPE_A_PSR_STATUS_VLV (1UL<<6) #define PIPE_LEGACY_BLC_EVENT_STATUS (1UL<<6) #define PIPE_ODD_FIELD_INTERRUPT_STATUS (1UL<<5) @@ -3842,73 +3892,151 @@ enum punit_power_well { #define DSPARB_BEND_SHIFT 9 /* on 855 */ #define DSPARB_AEND_SHIFT 0 +/* pnv/gen4/g4x/vlv/chv */ #define DSPFW1 (dev_priv->info.display_mmio_offset + 0x70034) -#define DSPFW_SR_SHIFT 23 -#define DSPFW_SR_MASK (0x1ff<<23) -#define DSPFW_CURSORB_SHIFT 16 -#define DSPFW_CURSORB_MASK (0x3f<<16) -#define DSPFW_PLANEB_SHIFT 8 -#define DSPFW_PLANEB_MASK (0x7f<<8) -#define DSPFW_PLANEA_MASK (0x7f) +#define DSPFW_SR_SHIFT 23 +#define DSPFW_SR_MASK (0x1ff<<23) +#define DSPFW_CURSORB_SHIFT 16 +#define DSPFW_CURSORB_MASK (0x3f<<16) +#define DSPFW_PLANEB_SHIFT 8 +#define DSPFW_PLANEB_MASK (0x7f<<8) +#define DSPFW_PLANEB_MASK_VLV (0xff<<8) /* vlv/chv */ +#define DSPFW_PLANEA_SHIFT 0 +#define DSPFW_PLANEA_MASK (0x7f<<0) +#define DSPFW_PLANEA_MASK_VLV (0xff<<0) /* vlv/chv */ #define DSPFW2 (dev_priv->info.display_mmio_offset + 0x70038) -#define DSPFW_CURSORA_MASK 0x00003f00 -#define DSPFW_CURSORA_SHIFT 8 -#define DSPFW_PLANEC_MASK (0x7f) +#define DSPFW_FBC_SR_EN (1<<31) /* g4x */ +#define DSPFW_FBC_SR_SHIFT 28 +#define DSPFW_FBC_SR_MASK (0x7<<28) /* g4x */ +#define DSPFW_FBC_HPLL_SR_SHIFT 24 +#define DSPFW_FBC_HPLL_SR_MASK (0xf<<24) /* g4x */ +#define DSPFW_SPRITEB_SHIFT (16) +#define DSPFW_SPRITEB_MASK (0x7f<<16) /* g4x */ +#define DSPFW_SPRITEB_MASK_VLV (0xff<<16) /* vlv/chv */ +#define DSPFW_CURSORA_SHIFT 8 +#define DSPFW_CURSORA_MASK (0x3f<<8) +#define DSPFW_PLANEC_SHIFT_OLD 0 +#define DSPFW_PLANEC_MASK_OLD (0x7f<<0) /* pre-gen4 sprite C */ +#define DSPFW_SPRITEA_SHIFT 0 +#define DSPFW_SPRITEA_MASK (0x7f<<0) /* g4x */ +#define DSPFW_SPRITEA_MASK_VLV (0xff<<0) /* vlv/chv */ #define DSPFW3 (dev_priv->info.display_mmio_offset + 0x7003c) -#define DSPFW_HPLL_SR_EN (1<<31) -#define DSPFW_CURSOR_SR_SHIFT 24 +#define DSPFW_HPLL_SR_EN (1<<31) #define PINEVIEW_SELF_REFRESH_EN (1<<30) +#define DSPFW_CURSOR_SR_SHIFT 24 #define DSPFW_CURSOR_SR_MASK (0x3f<<24) #define DSPFW_HPLL_CURSOR_SHIFT 16 #define DSPFW_HPLL_CURSOR_MASK (0x3f<<16) -#define DSPFW_HPLL_SR_MASK (0x1ff) -#define DSPFW4 (dev_priv->info.display_mmio_offset + 0x70070) -#define DSPFW7 (dev_priv->info.display_mmio_offset + 0x7007c) +#define DSPFW_HPLL_SR_SHIFT 0 +#define DSPFW_HPLL_SR_MASK (0x1ff<<0) + +/* vlv/chv */ +#define DSPFW4 (VLV_DISPLAY_BASE + 0x70070) +#define DSPFW_SPRITEB_WM1_SHIFT 16 +#define DSPFW_SPRITEB_WM1_MASK (0xff<<16) +#define DSPFW_CURSORA_WM1_SHIFT 8 +#define DSPFW_CURSORA_WM1_MASK (0x3f<<8) +#define DSPFW_SPRITEA_WM1_SHIFT 0 +#define DSPFW_SPRITEA_WM1_MASK (0xff<<0) +#define DSPFW5 (VLV_DISPLAY_BASE + 0x70074) +#define DSPFW_PLANEB_WM1_SHIFT 24 +#define DSPFW_PLANEB_WM1_MASK (0xff<<24) +#define DSPFW_PLANEA_WM1_SHIFT 16 +#define DSPFW_PLANEA_WM1_MASK (0xff<<16) +#define DSPFW_CURSORB_WM1_SHIFT 8 +#define DSPFW_CURSORB_WM1_MASK (0x3f<<8) +#define DSPFW_CURSOR_SR_WM1_SHIFT 0 +#define DSPFW_CURSOR_SR_WM1_MASK (0x3f<<0) +#define DSPFW6 (VLV_DISPLAY_BASE + 0x70078) +#define DSPFW_SR_WM1_SHIFT 0 +#define DSPFW_SR_WM1_MASK (0x1ff<<0) +#define DSPFW7 (VLV_DISPLAY_BASE + 0x7007c) +#define DSPFW7_CHV (VLV_DISPLAY_BASE + 0x700b4) /* wtf #1? */ +#define DSPFW_SPRITED_WM1_SHIFT 24 +#define DSPFW_SPRITED_WM1_MASK (0xff<<24) +#define DSPFW_SPRITED_SHIFT 16 +#define DSPFW_SPRITED_MASK (0xff<<16) +#define DSPFW_SPRITEC_WM1_SHIFT 8 +#define DSPFW_SPRITEC_WM1_MASK (0xff<<8) +#define DSPFW_SPRITEC_SHIFT 0 +#define DSPFW_SPRITEC_MASK (0xff<<0) +#define DSPFW8_CHV (VLV_DISPLAY_BASE + 0x700b8) +#define DSPFW_SPRITEF_WM1_SHIFT 24 +#define DSPFW_SPRITEF_WM1_MASK (0xff<<24) +#define DSPFW_SPRITEF_SHIFT 16 +#define DSPFW_SPRITEF_MASK (0xff<<16) +#define DSPFW_SPRITEE_WM1_SHIFT 8 +#define DSPFW_SPRITEE_WM1_MASK (0xff<<8) +#define DSPFW_SPRITEE_SHIFT 0 +#define DSPFW_SPRITEE_MASK (0xff<<0) +#define DSPFW9_CHV (VLV_DISPLAY_BASE + 0x7007c) /* wtf #2? */ +#define DSPFW_PLANEC_WM1_SHIFT 24 +#define DSPFW_PLANEC_WM1_MASK (0xff<<24) +#define DSPFW_PLANEC_SHIFT 16 +#define DSPFW_PLANEC_MASK (0xff<<16) +#define DSPFW_CURSORC_WM1_SHIFT 8 +#define DSPFW_CURSORC_WM1_MASK (0x3f<<16) +#define DSPFW_CURSORC_SHIFT 0 +#define DSPFW_CURSORC_MASK (0x3f<<0) + +/* vlv/chv high order bits */ +#define DSPHOWM (VLV_DISPLAY_BASE + 0x70064) +#define DSPFW_SR_HI_SHIFT 24 +#define DSPFW_SR_HI_MASK (1<<24) +#define DSPFW_SPRITEF_HI_SHIFT 23 +#define DSPFW_SPRITEF_HI_MASK (1<<23) +#define DSPFW_SPRITEE_HI_SHIFT 22 +#define DSPFW_SPRITEE_HI_MASK (1<<22) +#define DSPFW_PLANEC_HI_SHIFT 21 +#define DSPFW_PLANEC_HI_MASK (1<<21) +#define DSPFW_SPRITED_HI_SHIFT 20 +#define DSPFW_SPRITED_HI_MASK (1<<20) +#define DSPFW_SPRITEC_HI_SHIFT 16 +#define DSPFW_SPRITEC_HI_MASK (1<<16) +#define DSPFW_PLANEB_HI_SHIFT 12 +#define DSPFW_PLANEB_HI_MASK (1<<12) +#define DSPFW_SPRITEB_HI_SHIFT 8 +#define DSPFW_SPRITEB_HI_MASK (1<<8) +#define DSPFW_SPRITEA_HI_SHIFT 4 +#define DSPFW_SPRITEA_HI_MASK (1<<4) +#define DSPFW_PLANEA_HI_SHIFT 0 +#define DSPFW_PLANEA_HI_MASK (1<<0) +#define DSPHOWM1 (VLV_DISPLAY_BASE + 0x70068) +#define DSPFW_SR_WM1_HI_SHIFT 24 +#define DSPFW_SR_WM1_HI_MASK (1<<24) +#define DSPFW_SPRITEF_WM1_HI_SHIFT 23 +#define DSPFW_SPRITEF_WM1_HI_MASK (1<<23) +#define DSPFW_SPRITEE_WM1_HI_SHIFT 22 +#define DSPFW_SPRITEE_WM1_HI_MASK (1<<22) +#define DSPFW_PLANEC_WM1_HI_SHIFT 21 +#define DSPFW_PLANEC_WM1_HI_MASK (1<<21) +#define DSPFW_SPRITED_WM1_HI_SHIFT 20 +#define DSPFW_SPRITED_WM1_HI_MASK (1<<20) +#define DSPFW_SPRITEC_WM1_HI_SHIFT 16 +#define DSPFW_SPRITEC_WM1_HI_MASK (1<<16) +#define DSPFW_PLANEB_WM1_HI_SHIFT 12 +#define DSPFW_PLANEB_WM1_HI_MASK (1<<12) +#define DSPFW_SPRITEB_WM1_HI_SHIFT 8 +#define DSPFW_SPRITEB_WM1_HI_MASK (1<<8) +#define DSPFW_SPRITEA_WM1_HI_SHIFT 4 +#define DSPFW_SPRITEA_WM1_HI_MASK (1<<4) +#define DSPFW_PLANEA_WM1_HI_SHIFT 0 +#define DSPFW_PLANEA_WM1_HI_MASK (1<<0) /* drain latency register values*/ #define DRAIN_LATENCY_PRECISION_32 32 #define DRAIN_LATENCY_PRECISION_64 64 -#define VLV_DDL1 (VLV_DISPLAY_BASE + 0x70050) -#define DDL_CURSORA_PRECISION_64 (1<<31) -#define DDL_CURSORA_PRECISION_32 (0<<31) -#define DDL_CURSORA_SHIFT 24 -#define DDL_SPRITEB_PRECISION_64 (1<<23) -#define DDL_SPRITEB_PRECISION_32 (0<<23) -#define DDL_SPRITEB_SHIFT 16 -#define DDL_SPRITEA_PRECISION_64 (1<<15) -#define DDL_SPRITEA_PRECISION_32 (0<<15) -#define DDL_SPRITEA_SHIFT 8 -#define DDL_PLANEA_PRECISION_64 (1<<7) -#define DDL_PLANEA_PRECISION_32 (0<<7) -#define DDL_PLANEA_SHIFT 0 - -#define VLV_DDL2 (VLV_DISPLAY_BASE + 0x70054) -#define DDL_CURSORB_PRECISION_64 (1<<31) -#define DDL_CURSORB_PRECISION_32 (0<<31) -#define DDL_CURSORB_SHIFT 24 -#define DDL_SPRITED_PRECISION_64 (1<<23) -#define DDL_SPRITED_PRECISION_32 (0<<23) -#define DDL_SPRITED_SHIFT 16 -#define DDL_SPRITEC_PRECISION_64 (1<<15) -#define DDL_SPRITEC_PRECISION_32 (0<<15) -#define DDL_SPRITEC_SHIFT 8 -#define DDL_PLANEB_PRECISION_64 (1<<7) -#define DDL_PLANEB_PRECISION_32 (0<<7) -#define DDL_PLANEB_SHIFT 0 - -#define VLV_DDL3 (VLV_DISPLAY_BASE + 0x70058) -#define DDL_CURSORC_PRECISION_64 (1<<31) -#define DDL_CURSORC_PRECISION_32 (0<<31) -#define DDL_CURSORC_SHIFT 24 -#define DDL_SPRITEF_PRECISION_64 (1<<23) -#define DDL_SPRITEF_PRECISION_32 (0<<23) -#define DDL_SPRITEF_SHIFT 16 -#define DDL_SPRITEE_PRECISION_64 (1<<15) -#define DDL_SPRITEE_PRECISION_32 (0<<15) -#define DDL_SPRITEE_SHIFT 8 -#define DDL_PLANEC_PRECISION_64 (1<<7) -#define DDL_PLANEC_PRECISION_32 (0<<7) -#define DDL_PLANEC_SHIFT 0 +#define VLV_DDL(pipe) (VLV_DISPLAY_BASE + 0x70050 + 4 * (pipe)) +#define DDL_CURSOR_PRECISION_64 (1<<31) +#define DDL_CURSOR_PRECISION_32 (0<<31) +#define DDL_CURSOR_SHIFT 24 +#define DDL_SPRITE_PRECISION_64(sprite) (1<<(15+8*(sprite))) +#define DDL_SPRITE_PRECISION_32(sprite) (0<<(15+8*(sprite))) +#define DDL_SPRITE_SHIFT(sprite) (8+8*(sprite)) +#define DDL_PLANE_PRECISION_64 (1<<7) +#define DDL_PLANE_PRECISION_32 (0<<7) +#define DDL_PLANE_SHIFT 0 +#define DRAIN_LATENCY_MASK 0x7f /* FIFO watermark sizes etc */ #define G4X_FIFO_LINE_SIZE 64 @@ -4026,7 +4154,8 @@ enum punit_power_well { /* Old style CUR*CNTR flags (desktop 8xx) */ #define CURSOR_ENABLE 0x80000000 #define CURSOR_GAMMA_ENABLE 0x40000000 -#define CURSOR_STRIDE_MASK 0x30000000 +#define CURSOR_STRIDE_SHIFT 28 +#define CURSOR_STRIDE(x) ((ffs(x)-9) << CURSOR_STRIDE_SHIFT) /* 256,512,1k,2k */ #define CURSOR_PIPE_CSC_ENABLE (1<<24) #define CURSOR_FORMAT_SHIFT 24 #define CURSOR_FORMAT_MASK (0x07 << CURSOR_FORMAT_SHIFT) @@ -4111,6 +4240,7 @@ enum punit_power_well { #define DISPPLANE_NO_LINE_DOUBLE 0 #define DISPPLANE_STEREO_POLARITY_FIRST 0 #define DISPPLANE_STEREO_POLARITY_SECOND (1<<18) +#define DISPPLANE_ROTATE_180 (1<<15) #define DISPPLANE_TRICKLE_FEED_DISABLE (1<<14) /* Ironlake */ #define DISPPLANE_TILED (1<<10) #define _DSPAADDR 0x70184 @@ -4195,6 +4325,7 @@ enum punit_power_well { #define DVS_YUV_ORDER_UYVY (1<<16) #define DVS_YUV_ORDER_YVYU (2<<16) #define DVS_YUV_ORDER_VYUY (3<<16) +#define DVS_ROTATE_180 (1<<15) #define DVS_DEST_KEY (1<<2) #define DVS_TRICKLE_FEED_DISABLE (1<<14) #define DVS_TILED (1<<10) @@ -4265,6 +4396,7 @@ enum punit_power_well { #define SPRITE_YUV_ORDER_UYVY (1<<16) #define SPRITE_YUV_ORDER_YVYU (2<<16) #define SPRITE_YUV_ORDER_VYUY (3<<16) +#define SPRITE_ROTATE_180 (1<<15) #define SPRITE_TRICKLE_FEED_DISABLE (1<<14) #define SPRITE_INT_GAMMA_ENABLE (1<<13) #define SPRITE_TILED (1<<10) @@ -4338,6 +4470,7 @@ enum punit_power_well { #define SP_YUV_ORDER_UYVY (1<<16) #define SP_YUV_ORDER_YVYU (2<<16) #define SP_YUV_ORDER_VYUY (3<<16) +#define SP_ROTATE_180 (1<<15) #define SP_TILED (1<<10) #define _SPALINOFF (VLV_DISPLAY_BASE + 0x72184) #define _SPASTRIDE (VLV_DISPLAY_BASE + 0x72188) @@ -5246,8 +5379,7 @@ enum punit_power_well { #define PIPEA_PP_STATUS (VLV_DISPLAY_BASE + 0x61200) #define PIPEA_PP_CONTROL (VLV_DISPLAY_BASE + 0x61204) #define PIPEA_PP_ON_DELAYS (VLV_DISPLAY_BASE + 0x61208) -#define PANEL_PORT_SELECT_DPB_VLV (1 << 30) -#define PANEL_PORT_SELECT_DPC_VLV (2 << 30) +#define PANEL_PORT_SELECT_VLV(port) ((port) << 30) #define PIPEA_PP_OFF_DELAYS (VLV_DISPLAY_BASE + 0x6120c) #define PIPEA_PP_DIVISOR (VLV_DISPLAY_BASE + 0x61210) @@ -5407,7 +5539,6 @@ enum punit_power_well { #define VLV_GTLC_ALLOWWAKEERR (1 << 1) #define VLV_GTLC_PW_MEDIA_STATUS_MASK (1 << 5) #define VLV_GTLC_PW_RENDER_STATUS_MASK (1 << 7) -#define VLV_GTLC_SURVIVABILITY_REG 0x130098 #define FORCEWAKE_MT 0xa188 /* multi-threaded */ #define FORCEWAKE_KERNEL 0x1 #define FORCEWAKE_USER 0x2 @@ -5545,12 +5676,6 @@ enum punit_power_well { GEN6_PM_RP_DOWN_THRESHOLD | \ GEN6_PM_RP_DOWN_TIMEOUT) -#define CHV_CZ_CLOCK_FREQ_MODE_200 200 -#define CHV_CZ_CLOCK_FREQ_MODE_267 267 -#define CHV_CZ_CLOCK_FREQ_MODE_320 320 -#define CHV_CZ_CLOCK_FREQ_MODE_333 333 -#define CHV_CZ_CLOCK_FREQ_MODE_400 400 - #define GEN7_GT_SCRATCH_BASE 0x4F100 #define GEN7_GT_SCRATCH_REG_NUM 8 @@ -5866,15 +5991,7 @@ enum punit_power_well { #define DDI_BUF_CTL_B 0x64100 #define DDI_BUF_CTL(port) _PORT(port, DDI_BUF_CTL_A, DDI_BUF_CTL_B) #define DDI_BUF_CTL_ENABLE (1<<31) -#define DDI_BUF_EMP_400MV_0DB_HSW (0<<24) /* Sel0 */ -#define DDI_BUF_EMP_400MV_3_5DB_HSW (1<<24) /* Sel1 */ -#define DDI_BUF_EMP_400MV_6DB_HSW (2<<24) /* Sel2 */ -#define DDI_BUF_EMP_400MV_9_5DB_HSW (3<<24) /* Sel3 */ -#define DDI_BUF_EMP_600MV_0DB_HSW (4<<24) /* Sel4 */ -#define DDI_BUF_EMP_600MV_3_5DB_HSW (5<<24) /* Sel5 */ -#define DDI_BUF_EMP_600MV_6DB_HSW (6<<24) /* Sel6 */ -#define DDI_BUF_EMP_800MV_0DB_HSW (7<<24) /* Sel7 */ -#define DDI_BUF_EMP_800MV_3_5DB_HSW (8<<24) /* Sel8 */ +#define DDI_BUF_TRANS_SELECT(n) ((n) << 24) #define DDI_BUF_EMP_MASK (0xf<<24) #define DDI_BUF_PORT_REVERSAL (1<<16) #define DDI_BUF_IS_IDLE (1<<7) diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index ae7fd8fc27f05cab33eea39739c96f816f156fe9..503847f18fdd5a1252403ca44d85de7daab6fa82 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -540,7 +540,7 @@ static ssize_t error_state_read(struct file *filp, struct kobject *kobj, memset(&error_priv, 0, sizeof(error_priv)); - ret = i915_error_state_buf_init(&error_str, count, off); + ret = i915_error_state_buf_init(&error_str, to_i915(dev), count, off); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index afcc8dd40bdd437fa32c113d6aa966fb87bf3a1c..a4bd90f36a03396ea7e04854b2520a38e73918b2 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -627,16 +627,16 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb) switch (edp_link_params->preemphasis) { case EDP_PREEMPHASIS_NONE: - dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_0; + dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_0; break; case EDP_PREEMPHASIS_3_5dB: - dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5; + dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_1; break; case EDP_PREEMPHASIS_6dB: - dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_6; + dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_2; break; case EDP_PREEMPHASIS_9_5dB: - dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5; + dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_3; break; default: DRM_DEBUG_KMS("VBT has unknown eDP pre-emphasis value %u\n", @@ -646,16 +646,16 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb) switch (edp_link_params->vswing) { case EDP_VSWING_0_4V: - dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_400; + dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_0; break; case EDP_VSWING_0_6V: - dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_600; + dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_1; break; case EDP_VSWING_0_8V: - dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_800; + dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_2; break; case EDP_VSWING_1_2V: - dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_1200; + dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_3; break; default: DRM_DEBUG_KMS("VBT has unknown eDP voltage swing value %u\n", @@ -976,12 +976,10 @@ static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port, if (bdb->version >= 158) { /* The VBT HDMI level shift values match the table we have. */ hdmi_level_shift = child->raw[7] & 0xF; - if (hdmi_level_shift < 0xC) { - DRM_DEBUG_KMS("VBT HDMI level shift for port %c: %d\n", - port_name(port), - hdmi_level_shift); - info->hdmi_level_shift = hdmi_level_shift; - } + DRM_DEBUG_KMS("VBT HDMI level shift for port %c: %d\n", + port_name(port), + hdmi_level_shift); + info->hdmi_level_shift = hdmi_level_shift; } } @@ -1114,8 +1112,7 @@ init_vbt_defaults(struct drm_i915_private *dev_priv) struct ddi_vbt_port_info *info = &dev_priv->vbt.ddi_port_info[port]; - /* Recommended BSpec default: 800mV 0dB. */ - info->hdmi_level_shift = 6; + info->hdmi_level_shift = HDMI_LEVEL_SHIFT_UNKNOWN; info->supports_dvi = (port != PORT_A && port != PORT_E); info->supports_hdmi = info->supports_dvi; diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h index b98667796337d776b1d1d39c677a5dfffe6b32b6..905999bee2ac5ee7f7b550f1c3f4fa043f422a97 100644 --- a/drivers/gpu/drm/i915/intel_bios.h +++ b/drivers/gpu/drm/i915/intel_bios.h @@ -802,7 +802,8 @@ struct mipi_config { u16 rsvd4; - u8 rsvd5[5]; + u8 rsvd5; + u32 target_burst_mode_freq; u32 dsi_ddr_clk; u32 bridge_ref_clk; diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 5db0b5552e39a5005f1c9e1dde74fe7216bb5037..b63d4fa204a32b540529b9e49d72cb7108839190 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -28,87 +28,103 @@ #include "i915_drv.h" #include "intel_drv.h" +struct ddi_buf_trans { + u32 trans1; /* balance leg enable, de-emph level */ + u32 trans2; /* vref sel, vswing */ +}; + /* HDMI/DVI modes ignore everything but the last 2 items. So we share * them for both DP and FDI transports, allowing those ports to * automatically adapt to HDMI connections as well */ -static const u32 hsw_ddi_translations_dp[] = { - 0x00FFFFFF, 0x0006000E, /* DP parameters */ - 0x00D75FFF, 0x0005000A, - 0x00C30FFF, 0x00040006, - 0x80AAAFFF, 0x000B0000, - 0x00FFFFFF, 0x0005000A, - 0x00D75FFF, 0x000C0004, - 0x80C30FFF, 0x000B0000, - 0x00FFFFFF, 0x00040006, - 0x80D75FFF, 0x000B0000, +static const struct ddi_buf_trans hsw_ddi_translations_dp[] = { + { 0x00FFFFFF, 0x0006000E }, + { 0x00D75FFF, 0x0005000A }, + { 0x00C30FFF, 0x00040006 }, + { 0x80AAAFFF, 0x000B0000 }, + { 0x00FFFFFF, 0x0005000A }, + { 0x00D75FFF, 0x000C0004 }, + { 0x80C30FFF, 0x000B0000 }, + { 0x00FFFFFF, 0x00040006 }, + { 0x80D75FFF, 0x000B0000 }, }; -static const u32 hsw_ddi_translations_fdi[] = { - 0x00FFFFFF, 0x0007000E, /* FDI parameters */ - 0x00D75FFF, 0x000F000A, - 0x00C30FFF, 0x00060006, - 0x00AAAFFF, 0x001E0000, - 0x00FFFFFF, 0x000F000A, - 0x00D75FFF, 0x00160004, - 0x00C30FFF, 0x001E0000, - 0x00FFFFFF, 0x00060006, - 0x00D75FFF, 0x001E0000, +static const struct ddi_buf_trans hsw_ddi_translations_fdi[] = { + { 0x00FFFFFF, 0x0007000E }, + { 0x00D75FFF, 0x000F000A }, + { 0x00C30FFF, 0x00060006 }, + { 0x00AAAFFF, 0x001E0000 }, + { 0x00FFFFFF, 0x000F000A }, + { 0x00D75FFF, 0x00160004 }, + { 0x00C30FFF, 0x001E0000 }, + { 0x00FFFFFF, 0x00060006 }, + { 0x00D75FFF, 0x001E0000 }, }; -static const u32 hsw_ddi_translations_hdmi[] = { - /* Idx NT mV diff T mV diff db */ - 0x00FFFFFF, 0x0006000E, /* 0: 400 400 0 */ - 0x00E79FFF, 0x000E000C, /* 1: 400 500 2 */ - 0x00D75FFF, 0x0005000A, /* 2: 400 600 3.5 */ - 0x00FFFFFF, 0x0005000A, /* 3: 600 600 0 */ - 0x00E79FFF, 0x001D0007, /* 4: 600 750 2 */ - 0x00D75FFF, 0x000C0004, /* 5: 600 900 3.5 */ - 0x00FFFFFF, 0x00040006, /* 6: 800 800 0 */ - 0x80E79FFF, 0x00030002, /* 7: 800 1000 2 */ - 0x00FFFFFF, 0x00140005, /* 8: 850 850 0 */ - 0x00FFFFFF, 0x000C0004, /* 9: 900 900 0 */ - 0x00FFFFFF, 0x001C0003, /* 10: 950 950 0 */ - 0x80FFFFFF, 0x00030002, /* 11: 1000 1000 0 */ +static const struct ddi_buf_trans hsw_ddi_translations_hdmi[] = { + /* Idx NT mV d T mV d db */ + { 0x00FFFFFF, 0x0006000E }, /* 0: 400 400 0 */ + { 0x00E79FFF, 0x000E000C }, /* 1: 400 500 2 */ + { 0x00D75FFF, 0x0005000A }, /* 2: 400 600 3.5 */ + { 0x00FFFFFF, 0x0005000A }, /* 3: 600 600 0 */ + { 0x00E79FFF, 0x001D0007 }, /* 4: 600 750 2 */ + { 0x00D75FFF, 0x000C0004 }, /* 5: 600 900 3.5 */ + { 0x00FFFFFF, 0x00040006 }, /* 6: 800 800 0 */ + { 0x80E79FFF, 0x00030002 }, /* 7: 800 1000 2 */ + { 0x00FFFFFF, 0x00140005 }, /* 8: 850 850 0 */ + { 0x00FFFFFF, 0x000C0004 }, /* 9: 900 900 0 */ + { 0x00FFFFFF, 0x001C0003 }, /* 10: 950 950 0 */ + { 0x80FFFFFF, 0x00030002 }, /* 11: 1000 1000 0 */ }; -static const u32 bdw_ddi_translations_edp[] = { - 0x00FFFFFF, 0x00000012, /* eDP parameters */ - 0x00EBAFFF, 0x00020011, - 0x00C71FFF, 0x0006000F, - 0x00AAAFFF, 0x000E000A, - 0x00FFFFFF, 0x00020011, - 0x00DB6FFF, 0x0005000F, - 0x00BEEFFF, 0x000A000C, - 0x00FFFFFF, 0x0005000F, - 0x00DB6FFF, 0x000A000C, - 0x00FFFFFF, 0x00140006 /* HDMI parameters 800mV 0dB*/ +static const struct ddi_buf_trans bdw_ddi_translations_edp[] = { + { 0x00FFFFFF, 0x00000012 }, + { 0x00EBAFFF, 0x00020011 }, + { 0x00C71FFF, 0x0006000F }, + { 0x00AAAFFF, 0x000E000A }, + { 0x00FFFFFF, 0x00020011 }, + { 0x00DB6FFF, 0x0005000F }, + { 0x00BEEFFF, 0x000A000C }, + { 0x00FFFFFF, 0x0005000F }, + { 0x00DB6FFF, 0x000A000C }, }; -static const u32 bdw_ddi_translations_dp[] = { - 0x00FFFFFF, 0x0007000E, /* DP parameters */ - 0x00D75FFF, 0x000E000A, - 0x00BEFFFF, 0x00140006, - 0x80B2CFFF, 0x001B0002, - 0x00FFFFFF, 0x000E000A, - 0x00D75FFF, 0x00180004, - 0x80CB2FFF, 0x001B0002, - 0x00F7DFFF, 0x00180004, - 0x80D75FFF, 0x001B0002, - 0x00FFFFFF, 0x00140006 /* HDMI parameters 800mV 0dB*/ +static const struct ddi_buf_trans bdw_ddi_translations_dp[] = { + { 0x00FFFFFF, 0x0007000E }, + { 0x00D75FFF, 0x000E000A }, + { 0x00BEFFFF, 0x00140006 }, + { 0x80B2CFFF, 0x001B0002 }, + { 0x00FFFFFF, 0x000E000A }, + { 0x00D75FFF, 0x00180004 }, + { 0x80CB2FFF, 0x001B0002 }, + { 0x00F7DFFF, 0x00180004 }, + { 0x80D75FFF, 0x001B0002 }, }; -static const u32 bdw_ddi_translations_fdi[] = { - 0x00FFFFFF, 0x0001000E, /* FDI parameters */ - 0x00D75FFF, 0x0004000A, - 0x00C30FFF, 0x00070006, - 0x00AAAFFF, 0x000C0000, - 0x00FFFFFF, 0x0004000A, - 0x00D75FFF, 0x00090004, - 0x00C30FFF, 0x000C0000, - 0x00FFFFFF, 0x00070006, - 0x00D75FFF, 0x000C0000, - 0x00FFFFFF, 0x00140006 /* HDMI parameters 800mV 0dB*/ +static const struct ddi_buf_trans bdw_ddi_translations_fdi[] = { + { 0x00FFFFFF, 0x0001000E }, + { 0x00D75FFF, 0x0004000A }, + { 0x00C30FFF, 0x00070006 }, + { 0x00AAAFFF, 0x000C0000 }, + { 0x00FFFFFF, 0x0004000A }, + { 0x00D75FFF, 0x00090004 }, + { 0x00C30FFF, 0x000C0000 }, + { 0x00FFFFFF, 0x00070006 }, + { 0x00D75FFF, 0x000C0000 }, +}; + +static const struct ddi_buf_trans bdw_ddi_translations_hdmi[] = { + /* Idx NT mV d T mV df db */ + { 0x00FFFFFF, 0x0007000E }, /* 0: 400 400 0 */ + { 0x00D75FFF, 0x000E000A }, /* 1: 400 600 3.5 */ + { 0x00BEFFFF, 0x00140006 }, /* 2: 400 800 6 */ + { 0x00FFFFFF, 0x0009000D }, /* 3: 450 450 0 */ + { 0x00FFFFFF, 0x000E000A }, /* 4: 600 600 0 */ + { 0x00D7FFFF, 0x00140006 }, /* 5: 600 800 2.5 */ + { 0x80CB2FFF, 0x001B0002 }, /* 6: 600 1000 4.5 */ + { 0x00FFFFFF, 0x00140006 }, /* 7: 800 800 0 */ + { 0x80E79FFF, 0x001B0002 }, /* 8: 800 1000 2 */ + { 0x80FFFFFF, 0x001B0002 }, /* 9: 1000 1000 0 */ }; enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder) @@ -145,26 +161,36 @@ static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port) { struct drm_i915_private *dev_priv = dev->dev_private; u32 reg; - int i; + int i, n_hdmi_entries, hdmi_800mV_0dB; int hdmi_level = dev_priv->vbt.ddi_port_info[port].hdmi_level_shift; - const u32 *ddi_translations_fdi; - const u32 *ddi_translations_dp; - const u32 *ddi_translations_edp; - const u32 *ddi_translations; + const struct ddi_buf_trans *ddi_translations_fdi; + const struct ddi_buf_trans *ddi_translations_dp; + const struct ddi_buf_trans *ddi_translations_edp; + const struct ddi_buf_trans *ddi_translations_hdmi; + const struct ddi_buf_trans *ddi_translations; if (IS_BROADWELL(dev)) { ddi_translations_fdi = bdw_ddi_translations_fdi; ddi_translations_dp = bdw_ddi_translations_dp; ddi_translations_edp = bdw_ddi_translations_edp; + ddi_translations_hdmi = bdw_ddi_translations_hdmi; + n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi); + hdmi_800mV_0dB = 7; } else if (IS_HASWELL(dev)) { ddi_translations_fdi = hsw_ddi_translations_fdi; ddi_translations_dp = hsw_ddi_translations_dp; ddi_translations_edp = hsw_ddi_translations_dp; + ddi_translations_hdmi = hsw_ddi_translations_hdmi; + n_hdmi_entries = ARRAY_SIZE(hsw_ddi_translations_hdmi); + hdmi_800mV_0dB = 6; } else { WARN(1, "ddi translation table missing\n"); ddi_translations_edp = bdw_ddi_translations_dp; ddi_translations_fdi = bdw_ddi_translations_fdi; ddi_translations_dp = bdw_ddi_translations_dp; + ddi_translations_hdmi = bdw_ddi_translations_hdmi; + n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi); + hdmi_800mV_0dB = 7; } switch (port) { @@ -190,14 +216,22 @@ static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port) for (i = 0, reg = DDI_BUF_TRANS(port); i < ARRAY_SIZE(hsw_ddi_translations_fdi); i++) { - I915_WRITE(reg, ddi_translations[i]); + I915_WRITE(reg, ddi_translations[i].trans1); reg += 4; - } - /* Entry 9 is for HDMI: */ - for (i = 0; i < 2; i++) { - I915_WRITE(reg, hsw_ddi_translations_hdmi[hdmi_level * 2 + i]); + I915_WRITE(reg, ddi_translations[i].trans2); reg += 4; } + + /* Choose a good default if VBT is badly populated */ + if (hdmi_level == HDMI_LEVEL_SHIFT_UNKNOWN || + hdmi_level >= n_hdmi_entries) + hdmi_level = hdmi_800mV_0dB; + + /* Entry 9 is for HDMI: */ + I915_WRITE(reg, ddi_translations_hdmi[hdmi_level].trans1); + reg += 4; + I915_WRITE(reg, ddi_translations_hdmi[hdmi_level].trans2); + reg += 4; } /* Program DDI buffers translations for DP. By default, program ports A-D in DP @@ -214,18 +248,6 @@ void intel_prepare_ddi(struct drm_device *dev) intel_prepare_ddi_buffers(dev, port); } -static const long hsw_ddi_buf_ctl_values[] = { - DDI_BUF_EMP_400MV_0DB_HSW, - DDI_BUF_EMP_400MV_3_5DB_HSW, - DDI_BUF_EMP_400MV_6DB_HSW, - DDI_BUF_EMP_400MV_9_5DB_HSW, - DDI_BUF_EMP_600MV_0DB_HSW, - DDI_BUF_EMP_600MV_3_5DB_HSW, - DDI_BUF_EMP_600MV_6DB_HSW, - DDI_BUF_EMP_800MV_0DB_HSW, - DDI_BUF_EMP_800MV_3_5DB_HSW -}; - static void intel_wait_ddi_buf_idle(struct drm_i915_private *dev_priv, enum port port) { @@ -285,7 +307,7 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) /* Start the training iterating through available voltages and emphasis, * testing each value twice. */ - for (i = 0; i < ARRAY_SIZE(hsw_ddi_buf_ctl_values) * 2; i++) { + for (i = 0; i < ARRAY_SIZE(hsw_ddi_translations_fdi) * 2; i++) { /* Configure DP_TP_CTL with auto-training */ I915_WRITE(DP_TP_CTL(PORT_E), DP_TP_CTL_FDI_AUTOTRAIN | @@ -300,7 +322,7 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) I915_WRITE(DDI_BUF_CTL(PORT_E), DDI_BUF_CTL_ENABLE | ((intel_crtc->config.fdi_lanes - 1) << 1) | - hsw_ddi_buf_ctl_values[i / 2]); + DDI_BUF_TRANS_SELECT(i / 2)); POSTING_READ(DDI_BUF_CTL(PORT_E)); udelay(600); @@ -375,7 +397,7 @@ void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder) enc_to_dig_port(&encoder->base); intel_dp->DP = intel_dig_port->saved_port_bits | - DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW; + DDI_BUF_CTL_ENABLE | DDI_BUF_TRANS_SELECT(0); intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count); } @@ -402,7 +424,7 @@ intel_ddi_get_crtc_encoder(struct drm_crtc *crtc) } #define LC_FREQ 2700 -#define LC_FREQ_2K (LC_FREQ * 2000) +#define LC_FREQ_2K U64_C(LC_FREQ * 2000) #define P_MIN 2 #define P_MAX 64 @@ -414,7 +436,11 @@ intel_ddi_get_crtc_encoder(struct drm_crtc *crtc) #define VCO_MIN 2400 #define VCO_MAX 4800 -#define ABS_DIFF(a, b) ((a > b) ? (a - b) : (b - a)) +#define abs_diff(a, b) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + (void) (&__a == &__b); \ + __a > __b ? (__a - __b) : (__b - __a); }) struct wrpll_rnp { unsigned p, n2, r2; @@ -524,9 +550,9 @@ static void wrpll_update_rnp(uint64_t freq2k, unsigned budget, */ a = freq2k * budget * p * r2; b = freq2k * budget * best->p * best->r2; - diff = ABS_DIFF((freq2k * p * r2), (LC_FREQ_2K * n2)); - diff_best = ABS_DIFF((freq2k * best->p * best->r2), - (LC_FREQ_2K * best->n2)); + diff = abs_diff(freq2k * p * r2, LC_FREQ_2K * n2); + diff_best = abs_diff(freq2k * best->p * best->r2, + LC_FREQ_2K * best->n2); c = 1000000 * diff; d = 1000000 * diff_best; @@ -587,8 +613,8 @@ static int intel_ddi_calc_wrpll_link(struct drm_i915_private *dev_priv, return (refclk * n * 100) / (p * r); } -void intel_ddi_clock_get(struct intel_encoder *encoder, - struct intel_crtc_config *pipe_config) +static void hsw_ddi_clock_get(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) { struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; int link_clock = 0; @@ -643,9 +669,15 @@ void intel_ddi_clock_get(struct intel_encoder *encoder, pipe_config->adjusted_mode.crtc_clock = pipe_config->port_clock; } +void intel_ddi_clock_get(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) +{ + hsw_ddi_clock_get(encoder, pipe_config); +} + static void -intel_ddi_calculate_wrpll(int clock /* in Hz */, - unsigned *r2_out, unsigned *n2_out, unsigned *p_out) +hsw_ddi_calculate_wrpll(int clock /* in Hz */, + unsigned *r2_out, unsigned *n2_out, unsigned *p_out) { uint64_t freq2k; unsigned p, n2, r2; @@ -708,27 +740,17 @@ intel_ddi_calculate_wrpll(int clock /* in Hz */, *r2_out = best.r2; } -/* - * Tries to find a PLL for the CRTC. If it finds, it increases the refcount and - * stores it in intel_crtc->ddi_pll_sel, so other mode sets won't be able to - * steal the selected PLL. You need to call intel_ddi_pll_enable to actually - * enable the PLL. - */ -bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) +static bool +hsw_ddi_pll_select(struct intel_crtc *intel_crtc, + struct intel_encoder *intel_encoder, + int clock) { - struct drm_crtc *crtc = &intel_crtc->base; - struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc); - int type = intel_encoder->type; - int clock = intel_crtc->config.port_clock; - - intel_put_shared_dpll(intel_crtc); - - if (type == INTEL_OUTPUT_HDMI) { + if (intel_encoder->type == INTEL_OUTPUT_HDMI) { struct intel_shared_dpll *pll; uint32_t val; unsigned p, n2, r2; - intel_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p); + hsw_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p); val = WRPLL_PLL_ENABLE | WRPLL_PLL_LCPLL | WRPLL_DIVIDER_REFERENCE(r2) | WRPLL_DIVIDER_FEEDBACK(n2) | @@ -749,6 +771,25 @@ bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) return true; } + +/* + * Tries to find a *shared* PLL for the CRTC and store it in + * intel_crtc->ddi_pll_sel. + * + * For private DPLLs, compute_config() should do the selection for us. This + * function should be folded into compute_config() eventually. + */ +bool intel_ddi_pll_select(struct intel_crtc *intel_crtc) +{ + struct drm_crtc *crtc = &intel_crtc->base; + struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc); + int clock = intel_crtc->config.port_clock; + + intel_put_shared_dpll(intel_crtc); + + return hsw_ddi_pll_select(intel_crtc, intel_encoder, clock); +} + void intel_ddi_set_pipe_settings(struct drm_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->dev->dev_private; @@ -1183,31 +1224,52 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder) } } -int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv) +static int bdw_get_cdclk_freq(struct drm_i915_private *dev_priv) +{ + uint32_t lcpll = I915_READ(LCPLL_CTL); + uint32_t freq = lcpll & LCPLL_CLK_FREQ_MASK; + + if (lcpll & LCPLL_CD_SOURCE_FCLK) + return 800000; + else if (I915_READ(FUSE_STRAP) & HSW_CDCLK_LIMIT) + return 450000; + else if (freq == LCPLL_CLK_FREQ_450) + return 450000; + else if (freq == LCPLL_CLK_FREQ_54O_BDW) + return 540000; + else if (freq == LCPLL_CLK_FREQ_337_5_BDW) + return 337500; + else + return 675000; +} + +static int hsw_get_cdclk_freq(struct drm_i915_private *dev_priv) { struct drm_device *dev = dev_priv->dev; uint32_t lcpll = I915_READ(LCPLL_CTL); uint32_t freq = lcpll & LCPLL_CLK_FREQ_MASK; - if (lcpll & LCPLL_CD_SOURCE_FCLK) { + if (lcpll & LCPLL_CD_SOURCE_FCLK) return 800000; - } else if (I915_READ(FUSE_STRAP) & HSW_CDCLK_LIMIT) { + else if (I915_READ(FUSE_STRAP) & HSW_CDCLK_LIMIT) return 450000; - } else if (freq == LCPLL_CLK_FREQ_450) { + else if (freq == LCPLL_CLK_FREQ_450) return 450000; - } else if (IS_HASWELL(dev)) { - if (IS_ULT(dev)) - return 337500; - else - return 540000; - } else { - if (freq == LCPLL_CLK_FREQ_54O_BDW) - return 540000; - else if (freq == LCPLL_CLK_FREQ_337_5_BDW) - return 337500; - else - return 675000; - } + else if (IS_ULT(dev)) + return 337500; + else + return 540000; +} + +int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv) +{ + struct drm_device *dev = dev_priv->dev; + + if (IS_BROADWELL(dev)) + return bdw_get_cdclk_freq(dev_priv); + + /* Haswell */ + return hsw_get_cdclk_freq(dev_priv); } static void hsw_ddi_pll_enable(struct drm_i915_private *dev_priv, @@ -1248,10 +1310,8 @@ static const char * const hsw_ddi_pll_names[] = { "WRPLL 2", }; -void intel_ddi_pll_init(struct drm_device *dev) +static void hsw_shared_dplls_init(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = dev->dev_private; - uint32_t val = I915_READ(LCPLL_CTL); int i; dev_priv->num_shared_dpll = 2; @@ -1264,6 +1324,14 @@ void intel_ddi_pll_init(struct drm_device *dev) dev_priv->shared_dplls[i].get_hw_state = hsw_ddi_pll_get_hw_state; } +} + +void intel_ddi_pll_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t val = I915_READ(LCPLL_CTL); + + hsw_shared_dplls_init(dev_priv); /* The LCPLL register should be turned on by the BIOS. For now let's * just check its state and print errors in case something is wrong. @@ -1444,7 +1512,7 @@ void intel_ddi_get_config(struct intel_encoder *encoder, dev_priv->vbt.edp_bpp = pipe_config->pipe_bpp; } - intel_ddi_clock_get(encoder, pipe_config); + hsw_ddi_clock_get(encoder, pipe_config); } static void intel_ddi_destroy(struct drm_encoder *encoder) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d8324c69fa868de2364cf1b272426f5a634c5c31..507370513f3d2e8494f4d4ad89fe265b0128e295 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -91,15 +91,16 @@ static int intel_framebuffer_init(struct drm_device *dev, struct intel_framebuffer *ifb, struct drm_mode_fb_cmd2 *mode_cmd, struct drm_i915_gem_object *obj); -static void intel_dp_set_m_n(struct intel_crtc *crtc); static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc); static void intel_set_pipe_timings(struct intel_crtc *intel_crtc); static void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, - struct intel_link_m_n *m_n); + struct intel_link_m_n *m_n, + struct intel_link_m_n *m2_n2); static void ironlake_set_pipeconf(struct drm_crtc *crtc); static void haswell_set_pipeconf(struct drm_crtc *crtc); static void intel_set_pipe_csc(struct drm_crtc *crtc); static void vlv_prepare_pll(struct intel_crtc *crtc); +static void chv_prepare_pll(struct intel_crtc *crtc); static struct intel_encoder *intel_find_encoder(struct intel_connector *connector, int pipe) { @@ -899,7 +900,8 @@ static void g4x_wait_for_vblank(struct drm_device *dev, int pipe) frame = I915_READ(frame_reg); if (wait_for(I915_READ_NOTRACE(frame_reg) != frame, 50)) - WARN(1, "vblank wait timed out\n"); + WARN(1, "vblank wait on pipe %c timed out\n", + pipe_name(pipe)); } /** @@ -940,7 +942,8 @@ void intel_wait_for_vblank(struct drm_device *dev, int pipe) if (wait_for(I915_READ(pipestat_reg) & PIPE_VBLANK_INTERRUPT_STATUS, 50)) - DRM_DEBUG_KMS("vblank wait timed out\n"); + DRM_DEBUG_KMS("vblank wait on pipe %c timed out\n", + pipe_name(pipe)); } static bool pipe_dsl_stopped(struct drm_device *dev, enum pipe pipe) @@ -964,8 +967,7 @@ static bool pipe_dsl_stopped(struct drm_device *dev, enum pipe pipe) /* * intel_wait_for_pipe_off - wait for pipe to turn off - * @dev: drm device - * @pipe: pipe to wait for + * @crtc: crtc whose pipe to wait for * * After disabling a pipe, we can't wait for vblank in the usual way, * spinning on the vblank interrupt status bit, since we won't actually @@ -979,11 +981,12 @@ static bool pipe_dsl_stopped(struct drm_device *dev, enum pipe pipe) * ends up stopping at the start of the next frame). * */ -void intel_wait_for_pipe_off(struct drm_device *dev, int pipe) +static void intel_wait_for_pipe_off(struct intel_crtc *crtc) { + struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, - pipe); + enum transcoder cpu_transcoder = crtc->config.cpu_transcoder; + enum pipe pipe = crtc->pipe; if (INTEL_INFO(dev)->gen >= 4) { int reg = PIPECONF(cpu_transcoder); @@ -1192,27 +1195,40 @@ void assert_fdi_rx_pll(struct drm_i915_private *dev_priv, static void assert_panel_unlocked(struct drm_i915_private *dev_priv, enum pipe pipe) { - int pp_reg, lvds_reg; + struct drm_device *dev = dev_priv->dev; + int pp_reg; u32 val; enum pipe panel_pipe = PIPE_A; bool locked = true; - if (HAS_PCH_SPLIT(dev_priv->dev)) { + if (WARN_ON(HAS_DDI(dev))) + return; + + if (HAS_PCH_SPLIT(dev)) { + u32 port_sel; + pp_reg = PCH_PP_CONTROL; - lvds_reg = PCH_LVDS; + port_sel = I915_READ(PCH_PP_ON_DELAYS) & PANEL_PORT_SELECT_MASK; + + if (port_sel == PANEL_PORT_SELECT_LVDS && + I915_READ(PCH_LVDS) & LVDS_PIPEB_SELECT) + panel_pipe = PIPE_B; + /* XXX: else fix for eDP */ + } else if (IS_VALLEYVIEW(dev)) { + /* presumably write lock depends on pipe, not port select */ + pp_reg = VLV_PIPE_PP_CONTROL(pipe); + panel_pipe = pipe; } else { pp_reg = PP_CONTROL; - lvds_reg = LVDS; + if (I915_READ(LVDS) & LVDS_PIPEB_SELECT) + panel_pipe = PIPE_B; } val = I915_READ(pp_reg); if (!(val & PANEL_POWER_ON) || - ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS)) + ((val & PANEL_UNLOCK_MASK) == PANEL_UNLOCK_REGS)) locked = false; - if (I915_READ(lvds_reg) & LVDS_PIPEB_SELECT) - panel_pipe = PIPE_B; - WARN(panel_pipe == pipe && locked, "panel assertion failure, pipe %c regs locked\n", pipe_name(pipe)); @@ -1245,8 +1261,9 @@ void assert_pipe(struct drm_i915_private *dev_priv, enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); - /* if we need the pipe A quirk it must be always on */ - if (pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) + /* if we need the pipe quirk it must be always on */ + if ((pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) || + (pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE)) state = true; if (!intel_display_power_enabled(dev_priv, @@ -1300,7 +1317,7 @@ static void assert_planes_disabled(struct drm_i915_private *dev_priv, } /* Need to check both planes against the pipe */ - for_each_pipe(i) { + for_each_pipe(dev_priv, i) { reg = DSPCNTR(i); val = I915_READ(reg); cur_pipe = (val & DISPPLANE_SEL_PIPE_MASK) >> @@ -1341,6 +1358,12 @@ static void assert_sprites_disabled(struct drm_i915_private *dev_priv, } } +static void assert_vblank_disabled(struct drm_crtc *crtc) +{ + if (WARN_ON(drm_crtc_vblank_get(crtc) == 0)) + drm_crtc_vblank_put(crtc); +} + static void ibx_assert_pch_refclk_enabled(struct drm_i915_private *dev_priv) { u32 val; @@ -1513,34 +1536,6 @@ static void intel_init_dpio(struct drm_device *dev) } } -static void intel_reset_dpio(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - - if (IS_CHERRYVIEW(dev)) { - enum dpio_phy phy; - u32 val; - - for (phy = DPIO_PHY0; phy < I915_NUM_PHYS_VLV; phy++) { - /* Poll for phypwrgood signal */ - if (wait_for(I915_READ(DISPLAY_PHY_STATUS) & - PHY_POWERGOOD(phy), 1)) - DRM_ERROR("Display PHY %d is not power up\n", phy); - - /* - * Deassert common lane reset for PHY. - * - * This should only be done on init and resume from S3 - * with both PLLs disabled, or we risk losing DPIO and - * PLL synchronization. - */ - val = I915_READ(DISPLAY_PHY_CONTROL); - I915_WRITE(DISPLAY_PHY_CONTROL, - PHY_COM_LANE_RESET_DEASSERT(phy, val)); - } - } -} - static void vlv_enable_pll(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev; @@ -1554,7 +1549,7 @@ static void vlv_enable_pll(struct intel_crtc *crtc) BUG_ON(!IS_VALLEYVIEW(dev_priv->dev)); /* PLL is protected by panel, make sure we can write it */ - if (IS_MOBILE(dev_priv->dev) && !IS_I830(dev_priv->dev)) + if (IS_MOBILE(dev_priv->dev)) assert_panel_unlocked(dev_priv, crtc->pipe); I915_WRITE(reg, dpll); @@ -1617,6 +1612,18 @@ static void chv_enable_pll(struct intel_crtc *crtc) mutex_unlock(&dev_priv->dpio_lock); } +static int intel_num_dvo_pipes(struct drm_device *dev) +{ + struct intel_crtc *crtc; + int count = 0; + + for_each_intel_crtc(dev, crtc) + count += crtc->active && + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DVO); + + return count; +} + static void i9xx_enable_pll(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev; @@ -1633,7 +1640,18 @@ static void i9xx_enable_pll(struct intel_crtc *crtc) if (IS_MOBILE(dev) && !IS_I830(dev)) assert_panel_unlocked(dev_priv, crtc->pipe); - I915_WRITE(reg, dpll); + /* Enable DVO 2x clock on both PLLs if necessary */ + if (IS_I830(dev) && intel_num_dvo_pipes(dev) > 0) { + /* + * It appears to be important that we don't enable this + * for the current pipe before otherwise configuring the + * PLL. No idea how this should be handled if multiple + * DVO outputs are enabled simultaneosly. + */ + dpll |= DPLL_DVO_2X_MODE; + I915_WRITE(DPLL(!crtc->pipe), + I915_READ(DPLL(!crtc->pipe)) | DPLL_DVO_2X_MODE); + } /* Wait for the clocks to stabilize. */ POSTING_READ(reg); @@ -1672,10 +1690,25 @@ static void i9xx_enable_pll(struct intel_crtc *crtc) * * Note! This is for pre-ILK only. */ -static void i9xx_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) +static void i9xx_disable_pll(struct intel_crtc *crtc) { - /* Don't disable pipe A or pipe A PLLs if needed */ - if (pipe == PIPE_A && (dev_priv->quirks & QUIRK_PIPEA_FORCE)) + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum pipe pipe = crtc->pipe; + + /* Disable DVO 2x clock on both PLLs if necessary */ + if (IS_I830(dev) && + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DVO) && + intel_num_dvo_pipes(dev) == 1) { + I915_WRITE(DPLL(PIPE_B), + I915_READ(DPLL(PIPE_B)) & ~DPLL_DVO_2X_MODE); + I915_WRITE(DPLL(PIPE_A), + I915_READ(DPLL(PIPE_A)) & ~DPLL_DVO_2X_MODE); + } + + /* Don't disable pipe or pipe PLLs if needed */ + if ((pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) || + (pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE)) return; /* Make sure the pipe isn't still relying on us */ @@ -1712,7 +1745,7 @@ static void chv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) assert_pipe_disabled(dev_priv, pipe); /* Set PLL en = 0 */ - val = DPLL_SSC_REF_CLOCK_CHV; + val = DPLL_SSC_REF_CLOCK_CHV | DPLL_REFA_CLK_ENABLE_VLV; if (pipe != PIPE_A) val |= DPLL_INTEGRATED_CRI_CLK_VLV; I915_WRITE(DPLL(pipe), val); @@ -1806,7 +1839,7 @@ static void intel_enable_shared_dpll(struct intel_crtc *crtc) if (WARN_ON(pll->refcount == 0)) return; - DRM_DEBUG_KMS("enable %s (active %d, on? %d)for crtc %d\n", + DRM_DEBUG_KMS("enable %s (active %d, on? %d) for crtc %d\n", pll->name, pll->active, pll->on, crtc->base.base.id); @@ -1824,7 +1857,7 @@ static void intel_enable_shared_dpll(struct intel_crtc *crtc) pll->on = true; } -void intel_disable_shared_dpll(struct intel_crtc *crtc) +static void intel_disable_shared_dpll(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -1868,7 +1901,7 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv, uint32_t reg, val, pipeconf_val; /* PCH only available on ILK+ */ - BUG_ON(INTEL_INFO(dev)->gen < 5); + BUG_ON(!HAS_PCH_SPLIT(dev)); /* Make sure PCH DPLL is enabled */ assert_shared_dpll_enabled(dev_priv, @@ -1921,7 +1954,7 @@ static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv, u32 val, pipeconf_val; /* PCH only available on ILK+ */ - BUG_ON(INTEL_INFO(dev_priv->dev)->gen < 5); + BUG_ON(!HAS_PCH_SPLIT(dev_priv->dev)); /* FDI must be feeding us bits for PCH ports */ assert_fdi_tx_enabled(dev_priv, (enum pipe) cpu_transcoder); @@ -2043,8 +2076,8 @@ static void intel_enable_pipe(struct intel_crtc *crtc) reg = PIPECONF(cpu_transcoder); val = I915_READ(reg); if (val & PIPECONF_ENABLE) { - WARN_ON(!(pipe == PIPE_A && - dev_priv->quirks & QUIRK_PIPEA_FORCE)); + WARN_ON(!((pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) || + (pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE))); return; } @@ -2054,21 +2087,19 @@ static void intel_enable_pipe(struct intel_crtc *crtc) /** * intel_disable_pipe - disable a pipe, asserting requirements - * @dev_priv: i915 private structure - * @pipe: pipe to disable + * @crtc: crtc whose pipes is to be disabled * - * Disable @pipe, making sure that various hardware specific requirements - * are met, if applicable, e.g. plane disabled, panel fitter off, etc. - * - * @pipe should be %PIPE_A or %PIPE_B. + * Disable the pipe of @crtc, making sure that various hardware + * specific requirements are met, if applicable, e.g. plane + * disabled, panel fitter off, etc. * * Will wait until the pipe has shut down before returning. */ -static void intel_disable_pipe(struct drm_i915_private *dev_priv, - enum pipe pipe) +static void intel_disable_pipe(struct intel_crtc *crtc) { - enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, - pipe); + struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + enum transcoder cpu_transcoder = crtc->config.cpu_transcoder; + enum pipe pipe = crtc->pipe; int reg; u32 val; @@ -2080,17 +2111,26 @@ static void intel_disable_pipe(struct drm_i915_private *dev_priv, assert_cursor_disabled(dev_priv, pipe); assert_sprites_disabled(dev_priv, pipe); - /* Don't disable pipe A or pipe A PLLs if needed */ - if (pipe == PIPE_A && (dev_priv->quirks & QUIRK_PIPEA_FORCE)) - return; - reg = PIPECONF(cpu_transcoder); val = I915_READ(reg); if ((val & PIPECONF_ENABLE) == 0) return; - I915_WRITE(reg, val & ~PIPECONF_ENABLE); - intel_wait_for_pipe_off(dev_priv->dev, pipe); + /* + * Double wide has implications for planes + * so best keep it disabled when not needed. + */ + if (crtc->config.double_wide) + val &= ~PIPECONF_DOUBLE_WIDE; + + /* Don't disable pipe or pipe PLLs if needed */ + if (!(pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) && + !(pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE)) + val &= ~PIPECONF_ENABLE; + + I915_WRITE(reg, val); + if ((val & PIPECONF_ENABLE) == 0) + intel_wait_for_pipe_off(crtc); } /* @@ -2109,35 +2149,28 @@ void intel_flush_primary_plane(struct drm_i915_private *dev_priv, /** * intel_enable_primary_hw_plane - enable the primary plane on a given pipe - * @dev_priv: i915 private structure - * @plane: plane to enable - * @pipe: pipe being fed + * @plane: plane to be enabled + * @crtc: crtc for the plane * - * Enable @plane on @pipe, making sure that @pipe is running first. + * Enable @plane on @crtc, making sure that the pipe is running first. */ -static void intel_enable_primary_hw_plane(struct drm_i915_private *dev_priv, - enum plane plane, enum pipe pipe) +static void intel_enable_primary_hw_plane(struct drm_plane *plane, + struct drm_crtc *crtc) { - struct drm_device *dev = dev_priv->dev; - struct intel_crtc *intel_crtc = - to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); - int reg; - u32 val; + struct drm_device *dev = plane->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); /* If the pipe isn't enabled, we can't pump pixels and may hang */ - assert_pipe_enabled(dev_priv, pipe); + assert_pipe_enabled(dev_priv, intel_crtc->pipe); if (intel_crtc->primary_enabled) return; intel_crtc->primary_enabled = true; - reg = DSPCNTR(plane); - val = I915_READ(reg); - WARN_ON(val & DISPLAY_PLANE_ENABLE); - - I915_WRITE(reg, val | DISPLAY_PLANE_ENABLE); - intel_flush_primary_plane(dev_priv, plane); + dev_priv->display.update_primary_plane(crtc, plane->fb, + crtc->x, crtc->y); /* * BDW signals flip done immediately if the plane @@ -2150,31 +2183,27 @@ static void intel_enable_primary_hw_plane(struct drm_i915_private *dev_priv, /** * intel_disable_primary_hw_plane - disable the primary hardware plane - * @dev_priv: i915 private structure - * @plane: plane to disable - * @pipe: pipe consuming the data + * @plane: plane to be disabled + * @crtc: crtc for the plane * - * Disable @plane; should be an independent operation. + * Disable @plane on @crtc, making sure that the pipe is running first. */ -static void intel_disable_primary_hw_plane(struct drm_i915_private *dev_priv, - enum plane plane, enum pipe pipe) +static void intel_disable_primary_hw_plane(struct drm_plane *plane, + struct drm_crtc *crtc) { - struct intel_crtc *intel_crtc = - to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); - int reg; - u32 val; + struct drm_device *dev = plane->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + assert_pipe_enabled(dev_priv, intel_crtc->pipe); if (!intel_crtc->primary_enabled) return; intel_crtc->primary_enabled = false; - reg = DSPCNTR(plane); - val = I915_READ(reg); - WARN_ON((val & DISPLAY_PLANE_ENABLE) == 0); - - I915_WRITE(reg, val & ~DISPLAY_PLANE_ENABLE); - intel_flush_primary_plane(dev_priv, plane); + dev_priv->display.update_primary_plane(crtc, plane->fb, + crtc->x, crtc->y); } static bool need_vtd_wa(struct drm_device *dev) @@ -2422,16 +2451,46 @@ static void i9xx_update_primary_plane(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct drm_i915_gem_object *obj = intel_fb_obj(fb); + struct drm_i915_gem_object *obj; int plane = intel_crtc->plane; unsigned long linear_offset; u32 dspcntr; - u32 reg; + u32 reg = DSPCNTR(plane); + int pixel_size; + + if (!intel_crtc->primary_enabled) { + I915_WRITE(reg, 0); + if (INTEL_INFO(dev)->gen >= 4) + I915_WRITE(DSPSURF(plane), 0); + else + I915_WRITE(DSPADDR(plane), 0); + POSTING_READ(reg); + return; + } + + obj = intel_fb_obj(fb); + if (WARN_ON(obj == NULL)) + return; + + pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + + dspcntr = DISPPLANE_GAMMA_ENABLE; + + dspcntr |= DISPLAY_PLANE_ENABLE; + + if (INTEL_INFO(dev)->gen < 4) { + if (intel_crtc->pipe == PIPE_B) + dspcntr |= DISPPLANE_SEL_PIPE_B; + + /* pipesrc and dspsize control the size that is scaled from, + * which should always be the user's requested size. + */ + I915_WRITE(DSPSIZE(plane), + ((intel_crtc->config.pipe_src_h - 1) << 16) | + (intel_crtc->config.pipe_src_w - 1)); + I915_WRITE(DSPPOS(plane), 0); + } - reg = DSPCNTR(plane); - dspcntr = I915_READ(reg); - /* Mask out pixel format bits in case we change it */ - dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; switch (fb->pixel_format) { case DRM_FORMAT_C8: dspcntr |= DISPPLANE_8BPP; @@ -2463,30 +2522,40 @@ static void i9xx_update_primary_plane(struct drm_crtc *crtc, BUG(); } - if (INTEL_INFO(dev)->gen >= 4) { - if (obj->tiling_mode != I915_TILING_NONE) - dspcntr |= DISPPLANE_TILED; - else - dspcntr &= ~DISPPLANE_TILED; - } + if (INTEL_INFO(dev)->gen >= 4 && + obj->tiling_mode != I915_TILING_NONE) + dspcntr |= DISPPLANE_TILED; if (IS_G4X(dev)) dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; - I915_WRITE(reg, dspcntr); - - linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); + linear_offset = y * fb->pitches[0] + x * pixel_size; if (INTEL_INFO(dev)->gen >= 4) { intel_crtc->dspaddr_offset = intel_gen4_compute_page_offset(&x, &y, obj->tiling_mode, - fb->bits_per_pixel / 8, + pixel_size, fb->pitches[0]); linear_offset -= intel_crtc->dspaddr_offset; } else { intel_crtc->dspaddr_offset = linear_offset; } + if (to_intel_plane(crtc->primary)->rotation == BIT(DRM_ROTATE_180)) { + dspcntr |= DISPPLANE_ROTATE_180; + + x += (intel_crtc->config.pipe_src_w - 1); + y += (intel_crtc->config.pipe_src_h - 1); + + /* Finding the last pixel of the last line of the display + data and adding to linear_offset*/ + linear_offset += + (intel_crtc->config.pipe_src_h - 1) * fb->pitches[0] + + (intel_crtc->config.pipe_src_w - 1) * pixel_size; + } + + I915_WRITE(reg, dspcntr); + DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d %d\n", i915_gem_obj_ggtt_offset(obj), linear_offset, x, y, fb->pitches[0]); @@ -2508,16 +2577,33 @@ static void ironlake_update_primary_plane(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct drm_i915_gem_object *obj = intel_fb_obj(fb); + struct drm_i915_gem_object *obj; int plane = intel_crtc->plane; unsigned long linear_offset; u32 dspcntr; - u32 reg; + u32 reg = DSPCNTR(plane); + int pixel_size; + + if (!intel_crtc->primary_enabled) { + I915_WRITE(reg, 0); + I915_WRITE(DSPSURF(plane), 0); + POSTING_READ(reg); + return; + } + + obj = intel_fb_obj(fb); + if (WARN_ON(obj == NULL)) + return; + + pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + + dspcntr = DISPPLANE_GAMMA_ENABLE; + + dspcntr |= DISPLAY_PLANE_ENABLE; + + if (IS_HASWELL(dev) || IS_BROADWELL(dev)) + dspcntr |= DISPPLANE_PIPE_CSC_ENABLE; - reg = DSPCNTR(plane); - dspcntr = I915_READ(reg); - /* Mask out pixel format bits in case we change it */ - dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; switch (fb->pixel_format) { case DRM_FORMAT_C8: dspcntr |= DISPPLANE_8BPP; @@ -2547,22 +2633,32 @@ static void ironlake_update_primary_plane(struct drm_crtc *crtc, if (obj->tiling_mode != I915_TILING_NONE) dspcntr |= DISPPLANE_TILED; - else - dspcntr &= ~DISPPLANE_TILED; - if (IS_HASWELL(dev) || IS_BROADWELL(dev)) - dspcntr &= ~DISPPLANE_TRICKLE_FEED_DISABLE; - else + if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; - I915_WRITE(reg, dspcntr); - - linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); + linear_offset = y * fb->pitches[0] + x * pixel_size; intel_crtc->dspaddr_offset = intel_gen4_compute_page_offset(&x, &y, obj->tiling_mode, - fb->bits_per_pixel / 8, + pixel_size, fb->pitches[0]); linear_offset -= intel_crtc->dspaddr_offset; + if (to_intel_plane(crtc->primary)->rotation == BIT(DRM_ROTATE_180)) { + dspcntr |= DISPPLANE_ROTATE_180; + + if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) { + x += (intel_crtc->config.pipe_src_w - 1); + y += (intel_crtc->config.pipe_src_h - 1); + + /* Finding the last pixel of the last line of the display + data and adding to linear_offset*/ + linear_offset += + (intel_crtc->config.pipe_src_h - 1) * fb->pitches[0] + + (intel_crtc->config.pipe_src_w - 1) * pixel_size; + } + } + + I915_WRITE(reg, dspcntr); DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d %d\n", i915_gem_obj_ggtt_offset(obj), linear_offset, x, y, @@ -3346,23 +3442,54 @@ bool intel_has_pending_fb_unpin(struct drm_device *dev) return false; } +static void page_flip_completed(struct intel_crtc *intel_crtc) +{ + struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev); + struct intel_unpin_work *work = intel_crtc->unpin_work; + + /* ensure that the unpin work is consistent wrt ->pending. */ + smp_rmb(); + intel_crtc->unpin_work = NULL; + + if (work->event) + drm_send_vblank_event(intel_crtc->base.dev, + intel_crtc->pipe, + work->event); + + drm_crtc_vblank_put(&intel_crtc->base); + + wake_up_all(&dev_priv->pending_flip_queue); + queue_work(dev_priv->wq, &work->work); + + trace_i915_flip_complete(intel_crtc->plane, + work->pending_flip_obj); +} + void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; - if (crtc->primary->fb == NULL) - return; - WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue)); + if (WARN_ON(wait_event_timeout(dev_priv->pending_flip_queue, + !intel_crtc_has_pending_flip(crtc), + 60*HZ) == 0)) { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + unsigned long flags; - WARN_ON(wait_event_timeout(dev_priv->pending_flip_queue, - !intel_crtc_has_pending_flip(crtc), - 60*HZ) == 0); + spin_lock_irqsave(&dev->event_lock, flags); + if (intel_crtc->unpin_work) { + WARN_ONCE(1, "Removing stuck page flip\n"); + page_flip_completed(intel_crtc); + } + spin_unlock_irqrestore(&dev->event_lock, flags); + } - mutex_lock(&dev->struct_mutex); - intel_finish_fb(crtc->primary->fb); - mutex_unlock(&dev->struct_mutex); + if (crtc->primary->fb) { + mutex_lock(&dev->struct_mutex); + intel_finish_fb(crtc->primary->fb); + mutex_unlock(&dev->struct_mutex); + } } /* Program iCLKIP clock to the desired frequency */ @@ -3911,14 +4038,14 @@ static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable) static void intel_crtc_enable_planes(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; - int plane = intel_crtc->plane; + + assert_vblank_disabled(crtc); drm_vblank_on(dev, pipe); - intel_enable_primary_hw_plane(dev_priv, plane, pipe); + intel_enable_primary_hw_plane(crtc->primary, crtc); intel_enable_planes(crtc); intel_crtc_update_cursor(crtc, true); intel_crtc_dpms_overlay(intel_crtc, true); @@ -3955,7 +4082,7 @@ static void intel_crtc_disable_planes(struct drm_crtc *crtc) intel_crtc_dpms_overlay(intel_crtc, false); intel_crtc_update_cursor(crtc, false); intel_disable_planes(crtc); - intel_disable_primary_hw_plane(dev_priv, plane, pipe); + intel_disable_primary_hw_plane(crtc->primary, crtc); /* * FIXME: Once we grow proper nuclear flip support out of this we need @@ -3965,6 +4092,8 @@ static void intel_crtc_disable_planes(struct drm_crtc *crtc) intel_frontbuffer_flip(dev, INTEL_FRONTBUFFER_ALL_MASK(pipe)); drm_vblank_off(dev, pipe); + + assert_vblank_disabled(crtc); } static void ironlake_crtc_enable(struct drm_crtc *crtc) @@ -3974,7 +4103,6 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_encoder *encoder; int pipe = intel_crtc->pipe; - enum plane plane = intel_crtc->plane; WARN_ON(!crtc->enabled); @@ -3991,18 +4119,11 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) if (intel_crtc->config.has_pch_encoder) { intel_cpu_transcoder_set_m_n(intel_crtc, - &intel_crtc->config.fdi_m_n); + &intel_crtc->config.fdi_m_n, NULL); } ironlake_set_pipeconf(crtc); - /* Set up the display plane register */ - I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE); - POSTING_READ(DSPCNTR(plane)); - - dev_priv->display.update_primary_plane(crtc, crtc->primary->fb, - crtc->x, crtc->y); - intel_crtc->active = true; intel_set_cpu_fifo_underrun_reporting(dev, pipe, true); @@ -4087,7 +4208,6 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_encoder *encoder; int pipe = intel_crtc->pipe; - enum plane plane = intel_crtc->plane; WARN_ON(!crtc->enabled); @@ -4102,22 +4222,20 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) intel_set_pipe_timings(intel_crtc); + if (intel_crtc->config.cpu_transcoder != TRANSCODER_EDP) { + I915_WRITE(PIPE_MULT(intel_crtc->config.cpu_transcoder), + intel_crtc->config.pixel_multiplier - 1); + } + if (intel_crtc->config.has_pch_encoder) { intel_cpu_transcoder_set_m_n(intel_crtc, - &intel_crtc->config.fdi_m_n); + &intel_crtc->config.fdi_m_n, NULL); } haswell_set_pipeconf(crtc); intel_set_pipe_csc(crtc); - /* Set up the display plane register */ - I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE | DISPPLANE_PIPE_CSC_ENABLE); - POSTING_READ(DSPCNTR(plane)); - - dev_priv->display.update_primary_plane(crtc, crtc->primary->fb, - crtc->x, crtc->y); - intel_crtc->active = true; intel_set_cpu_fifo_underrun_reporting(dev, pipe, true); @@ -4198,7 +4316,8 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) if (intel_crtc->config.has_pch_encoder) intel_set_pch_fifo_underrun_reporting(dev, pipe, false); - intel_disable_pipe(dev_priv, pipe); + intel_disable_pipe(intel_crtc); + ironlake_pfit_disable(intel_crtc); for_each_encoder_on_crtc(dev, crtc, encoder) @@ -4246,7 +4365,6 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_encoder *encoder; - int pipe = intel_crtc->pipe; enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder; if (!intel_crtc->active) @@ -4261,7 +4379,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) if (intel_crtc->config.has_pch_encoder) intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, false); - intel_disable_pipe(dev_priv, pipe); + intel_disable_pipe(intel_crtc); if (intel_crtc->config.dp_encoder_is_mst) intel_ddi_set_vc_payload_alloc(crtc, false); @@ -4539,12 +4657,57 @@ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk) vlv_update_cdclk(dev); } +static void cherryview_set_cdclk(struct drm_device *dev, int cdclk) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 val, cmd; + + WARN_ON(dev_priv->display.get_display_clock_speed(dev) != dev_priv->vlv_cdclk_freq); + + switch (cdclk) { + case 400000: + cmd = 3; + break; + case 333333: + case 320000: + cmd = 2; + break; + case 266667: + cmd = 1; + break; + case 200000: + cmd = 0; + break; + default: + WARN_ON(1); + return; + } + + mutex_lock(&dev_priv->rps.hw_lock); + val = vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ); + val &= ~DSPFREQGUAR_MASK_CHV; + val |= (cmd << DSPFREQGUAR_SHIFT_CHV); + vlv_punit_write(dev_priv, PUNIT_REG_DSPFREQ, val); + if (wait_for((vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ) & + DSPFREQSTAT_MASK_CHV) == (cmd << DSPFREQSTAT_SHIFT_CHV), + 50)) { + DRM_ERROR("timed out waiting for CDclk change\n"); + } + mutex_unlock(&dev_priv->rps.hw_lock); + + vlv_update_cdclk(dev); +} + static int valleyview_calc_cdclk(struct drm_i915_private *dev_priv, int max_pixclk) { int vco = valleyview_get_vco(dev_priv); int freq_320 = (vco << 1) % 320000 != 0 ? 333333 : 320000; + /* FIXME: Punit isn't quite ready yet */ + if (IS_CHERRYVIEW(dev_priv->dev)) + return 400000; + /* * Really only a few cases to deal with, as only 4 CDclks are supported: * 200MHz @@ -4607,21 +4770,23 @@ static void valleyview_modeset_global_resources(struct drm_device *dev) int max_pixclk = intel_mode_max_pixclk(dev_priv); int req_cdclk = valleyview_calc_cdclk(dev_priv, max_pixclk); - if (req_cdclk != dev_priv->vlv_cdclk_freq) - valleyview_set_cdclk(dev, req_cdclk); + if (req_cdclk != dev_priv->vlv_cdclk_freq) { + if (IS_CHERRYVIEW(dev)) + cherryview_set_cdclk(dev, req_cdclk); + else + valleyview_set_cdclk(dev, req_cdclk); + } + modeset_update_crtc_power_domains(dev); } static void valleyview_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_encoder *encoder; int pipe = intel_crtc->pipe; - int plane = intel_crtc->plane; bool is_dsi; - u32 dspcntr; WARN_ON(!crtc->enabled); @@ -4630,33 +4795,20 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) is_dsi = intel_pipe_has_type(crtc, INTEL_OUTPUT_DSI); - if (!is_dsi && !IS_CHERRYVIEW(dev)) - vlv_prepare_pll(intel_crtc); - - /* Set up the display plane register */ - dspcntr = DISPPLANE_GAMMA_ENABLE; + if (!is_dsi) { + if (IS_CHERRYVIEW(dev)) + chv_prepare_pll(intel_crtc); + else + vlv_prepare_pll(intel_crtc); + } if (intel_crtc->config.has_dp_encoder) intel_dp_set_m_n(intel_crtc); intel_set_pipe_timings(intel_crtc); - /* pipesrc and dspsize control the size that is scaled from, - * which should always be the user's requested size. - */ - I915_WRITE(DSPSIZE(plane), - ((intel_crtc->config.pipe_src_h - 1) << 16) | - (intel_crtc->config.pipe_src_w - 1)); - I915_WRITE(DSPPOS(plane), 0); - i9xx_set_pipeconf(intel_crtc); - I915_WRITE(DSPCNTR(plane), dspcntr); - POSTING_READ(DSPCNTR(plane)); - - dev_priv->display.update_primary_plane(crtc, crtc->primary->fb, - crtc->x, crtc->y); - intel_crtc->active = true; intel_set_cpu_fifo_underrun_reporting(dev, pipe, true); @@ -4704,12 +4856,9 @@ static void i9xx_set_pll_dividers(struct intel_crtc *crtc) static void i9xx_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_encoder *encoder; int pipe = intel_crtc->pipe; - int plane = intel_crtc->plane; - u32 dspcntr; WARN_ON(!crtc->enabled); @@ -4718,35 +4867,13 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) i9xx_set_pll_dividers(intel_crtc); - /* Set up the display plane register */ - dspcntr = DISPPLANE_GAMMA_ENABLE; - - if (pipe == 0) - dspcntr &= ~DISPPLANE_SEL_PIPE_MASK; - else - dspcntr |= DISPPLANE_SEL_PIPE_B; - if (intel_crtc->config.has_dp_encoder) intel_dp_set_m_n(intel_crtc); intel_set_pipe_timings(intel_crtc); - /* pipesrc and dspsize control the size that is scaled from, - * which should always be the user's requested size. - */ - I915_WRITE(DSPSIZE(plane), - ((intel_crtc->config.pipe_src_h - 1) << 16) | - (intel_crtc->config.pipe_src_w - 1)); - I915_WRITE(DSPPOS(plane), 0); - i9xx_set_pipeconf(intel_crtc); - I915_WRITE(DSPCNTR(plane), dspcntr); - POSTING_READ(DSPCNTR(plane)); - - dev_priv->display.update_primary_plane(crtc, crtc->primary->fb, - crtc->x, crtc->y); - intel_crtc->active = true; if (!IS_GEN2(dev)) @@ -4842,7 +4969,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) */ intel_wait_for_vblank(dev, pipe); - intel_disable_pipe(dev_priv, pipe); + intel_disable_pipe(intel_crtc); i9xx_pfit_disable(intel_crtc); @@ -4856,7 +4983,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) else if (IS_VALLEYVIEW(dev)) vlv_disable_pll(dev_priv, pipe); else - i9xx_disable_pll(dev_priv, pipe); + i9xx_disable_pll(intel_crtc); } if (!IS_GEN2(dev)) @@ -5275,6 +5402,10 @@ static int valleyview_get_display_clock_speed(struct drm_device *dev) u32 val; int divider; + /* FIXME: Punit isn't quite ready yet */ + if (IS_CHERRYVIEW(dev)) + return 400000; + mutex_lock(&dev_priv->dpio_lock); val = vlv_cck_read(dev_priv, CCK_DISPLAY_CLOCK_CONTROL); mutex_unlock(&dev_priv->dpio_lock); @@ -5519,7 +5650,8 @@ static void intel_pch_transcoder_set_m_n(struct intel_crtc *crtc, } static void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, - struct intel_link_m_n *m_n) + struct intel_link_m_n *m_n, + struct intel_link_m_n *m2_n2) { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -5531,6 +5663,18 @@ static void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, I915_WRITE(PIPE_DATA_N1(transcoder), m_n->gmch_n); I915_WRITE(PIPE_LINK_M1(transcoder), m_n->link_m); I915_WRITE(PIPE_LINK_N1(transcoder), m_n->link_n); + /* M2_N2 registers to be set only for gen < 8 (M2_N2 available + * for gen < 8) and if DRRS is supported (to make sure the + * registers are not unnecessarily accessed). + */ + if (m2_n2 && INTEL_INFO(dev)->gen < 8 && + crtc->config.has_drrs) { + I915_WRITE(PIPE_DATA_M2(transcoder), + TU_SIZE(m2_n2->tu) | m2_n2->gmch_m); + I915_WRITE(PIPE_DATA_N2(transcoder), m2_n2->gmch_n); + I915_WRITE(PIPE_LINK_M2(transcoder), m2_n2->link_m); + I915_WRITE(PIPE_LINK_N2(transcoder), m2_n2->link_n); + } } else { I915_WRITE(PIPE_DATA_M_G4X(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); I915_WRITE(PIPE_DATA_N_G4X(pipe), m_n->gmch_n); @@ -5539,12 +5683,13 @@ static void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, } } -static void intel_dp_set_m_n(struct intel_crtc *crtc) +void intel_dp_set_m_n(struct intel_crtc *crtc) { if (crtc->config.has_pch_encoder) intel_pch_transcoder_set_m_n(crtc, &crtc->config.dp_m_n); else - intel_cpu_transcoder_set_m_n(crtc, &crtc->config.dp_m_n); + intel_cpu_transcoder_set_m_n(crtc, &crtc->config.dp_m_n, + &crtc->config.dp_m2_n2); } static void vlv_update_pll(struct intel_crtc *crtc) @@ -5661,6 +5806,18 @@ static void vlv_prepare_pll(struct intel_crtc *crtc) } static void chv_update_pll(struct intel_crtc *crtc) +{ + crtc->config.dpll_hw_state.dpll = DPLL_SSC_REF_CLOCK_CHV | + DPLL_REFA_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS | + DPLL_VCO_ENABLE; + if (crtc->pipe != PIPE_A) + crtc->config.dpll_hw_state.dpll |= DPLL_INTEGRATED_CRI_CLK_VLV; + + crtc->config.dpll_hw_state.dpll_md = + (crtc->config.pixel_multiplier - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT; +} + +static void chv_prepare_pll(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -5671,15 +5828,6 @@ static void chv_update_pll(struct intel_crtc *crtc) u32 bestn, bestm1, bestm2, bestp1, bestp2, bestm2_frac; int refclk; - crtc->config.dpll_hw_state.dpll = DPLL_SSC_REF_CLOCK_CHV | - DPLL_REFA_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS | - DPLL_VCO_ENABLE; - if (pipe != PIPE_A) - crtc->config.dpll_hw_state.dpll |= DPLL_INTEGRATED_CRI_CLK_VLV; - - crtc->config.dpll_hw_state.dpll_md = - (crtc->config.pixel_multiplier - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT; - bestn = crtc->config.dpll.n; bestm2_frac = crtc->config.dpll.m2 & 0x3fffff; bestm1 = crtc->config.dpll.m1; @@ -5839,7 +5987,7 @@ static void i8xx_update_pll(struct intel_crtc *crtc, dpll |= PLL_P2_DIVIDE_BY_4; } - if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DVO)) + if (!IS_I830(dev) && intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DVO)) dpll |= DPLL_DVO_2X_MODE; if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) && @@ -5990,9 +6138,9 @@ static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc) pipeconf = 0; - if (dev_priv->quirks & QUIRK_PIPEA_FORCE && - I915_READ(PIPECONF(intel_crtc->pipe)) & PIPECONF_ENABLE) - pipeconf |= PIPECONF_ENABLE; + if ((intel_crtc->pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) || + (intel_crtc->pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE)) + pipeconf |= I915_READ(PIPECONF(intel_crtc->pipe)) & PIPECONF_ENABLE; if (intel_crtc->config.double_wide) pipeconf |= PIPECONF_DOUBLE_WIDE; @@ -6235,7 +6383,7 @@ static void i9xx_get_plane_config(struct intel_crtc *crtc, crtc->base.primary->fb->height = ((val >> 0) & 0xfff) + 1; val = I915_READ(DSPSTRIDE(pipe)); - crtc->base.primary->fb->pitches[0] = val & 0xffffff80; + crtc->base.primary->fb->pitches[0] = val & 0xffffffc0; aligned_height = intel_align_height(dev, crtc->base.primary->fb->height, plane_config->tiled); @@ -6345,6 +6493,14 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc, } pipe_config->dpll_hw_state.dpll = I915_READ(DPLL(crtc->pipe)); if (!IS_VALLEYVIEW(dev)) { + /* + * DPLL_DVO_2X_MODE must be enabled for both DPLLs + * on 830. Filter it out here so that we don't + * report errors due to that. + */ + if (IS_I830(dev)) + pipe_config->dpll_hw_state.dpll &= ~DPLL_DVO_2X_MODE; + pipe_config->dpll_hw_state.fp0 = I915_READ(FP0(crtc->pipe)); pipe_config->dpll_hw_state.fp1 = I915_READ(FP1(crtc->pipe)); } else { @@ -6367,7 +6523,6 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc, static void ironlake_init_pch_refclk(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_mode_config *mode_config = &dev->mode_config; struct intel_encoder *encoder; u32 val, final; bool has_lvds = false; @@ -6377,8 +6532,7 @@ static void ironlake_init_pch_refclk(struct drm_device *dev) bool can_ssc = false; /* We need to take the global config into account */ - list_for_each_entry(encoder, &mode_config->encoder_list, - base.head) { + for_each_intel_encoder(dev, encoder) { switch (encoder->type) { case INTEL_OUTPUT_LVDS: has_panel = true; @@ -6685,11 +6839,10 @@ static void lpt_disable_clkout_dp(struct drm_device *dev) static void lpt_init_pch_refclk(struct drm_device *dev) { - struct drm_mode_config *mode_config = &dev->mode_config; struct intel_encoder *encoder; bool has_vga = false; - list_for_each_entry(encoder, &mode_config->encoder_list, base.head) { + for_each_intel_encoder(dev, encoder) { switch (encoder->type) { case INTEL_OUTPUT_ANALOG: has_vga = true; @@ -7145,7 +7298,8 @@ static void intel_pch_transcoder_get_m_n(struct intel_crtc *crtc, static void intel_cpu_transcoder_get_m_n(struct intel_crtc *crtc, enum transcoder transcoder, - struct intel_link_m_n *m_n) + struct intel_link_m_n *m_n, + struct intel_link_m_n *m2_n2) { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -7159,6 +7313,20 @@ static void intel_cpu_transcoder_get_m_n(struct intel_crtc *crtc, m_n->gmch_n = I915_READ(PIPE_DATA_N1(transcoder)); m_n->tu = ((I915_READ(PIPE_DATA_M1(transcoder)) & TU_SIZE_MASK) >> TU_SIZE_SHIFT) + 1; + /* Read M2_N2 registers only for gen < 8 (M2_N2 available for + * gen < 8) and if DRRS is supported (to make sure the + * registers are not unnecessarily read). + */ + if (m2_n2 && INTEL_INFO(dev)->gen < 8 && + crtc->config.has_drrs) { + m2_n2->link_m = I915_READ(PIPE_LINK_M2(transcoder)); + m2_n2->link_n = I915_READ(PIPE_LINK_N2(transcoder)); + m2_n2->gmch_m = I915_READ(PIPE_DATA_M2(transcoder)) + & ~TU_SIZE_MASK; + m2_n2->gmch_n = I915_READ(PIPE_DATA_N2(transcoder)); + m2_n2->tu = ((I915_READ(PIPE_DATA_M2(transcoder)) + & TU_SIZE_MASK) >> TU_SIZE_SHIFT) + 1; + } } else { m_n->link_m = I915_READ(PIPE_LINK_M_G4X(pipe)); m_n->link_n = I915_READ(PIPE_LINK_N_G4X(pipe)); @@ -7177,14 +7345,15 @@ void intel_dp_get_m_n(struct intel_crtc *crtc, intel_pch_transcoder_get_m_n(crtc, &pipe_config->dp_m_n); else intel_cpu_transcoder_get_m_n(crtc, pipe_config->cpu_transcoder, - &pipe_config->dp_m_n); + &pipe_config->dp_m_n, + &pipe_config->dp_m2_n2); } static void ironlake_get_fdi_m_n_config(struct intel_crtc *crtc, struct intel_crtc_config *pipe_config) { intel_cpu_transcoder_get_m_n(crtc, pipe_config->cpu_transcoder, - &pipe_config->fdi_m_n); + &pipe_config->fdi_m_n, NULL); } static void ironlake_get_pfit_config(struct intel_crtc *crtc, @@ -7255,7 +7424,7 @@ static void ironlake_get_plane_config(struct intel_crtc *crtc, crtc->base.primary->fb->height = ((val >> 0) & 0xfff) + 1; val = I915_READ(DSPSTRIDE(pipe)); - crtc->base.primary->fb->pitches[0] = val & 0xffffff80; + crtc->base.primary->fb->pitches[0] = val & 0xffffffc0; aligned_height = intel_align_height(dev, crtc->base.primary->fb->height, plane_config->tiled); @@ -7615,6 +7784,22 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, return 0; } +static void haswell_get_ddi_pll(struct drm_i915_private *dev_priv, + enum port port, + struct intel_crtc_config *pipe_config) +{ + pipe_config->ddi_pll_sel = I915_READ(PORT_CLK_SEL(port)); + + switch (pipe_config->ddi_pll_sel) { + case PORT_CLK_SEL_WRPLL1: + pipe_config->shared_dpll = DPLL_ID_WRPLL1; + break; + case PORT_CLK_SEL_WRPLL2: + pipe_config->shared_dpll = DPLL_ID_WRPLL2; + break; + } +} + static void haswell_get_ddi_port_state(struct intel_crtc *crtc, struct intel_crtc_config *pipe_config) { @@ -7628,16 +7813,7 @@ static void haswell_get_ddi_port_state(struct intel_crtc *crtc, port = (tmp & TRANS_DDI_PORT_MASK) >> TRANS_DDI_PORT_SHIFT; - pipe_config->ddi_pll_sel = I915_READ(PORT_CLK_SEL(port)); - - switch (pipe_config->ddi_pll_sel) { - case PORT_CLK_SEL_WRPLL1: - pipe_config->shared_dpll = DPLL_ID_WRPLL1; - break; - case PORT_CLK_SEL_WRPLL2: - pipe_config->shared_dpll = DPLL_ID_WRPLL2; - break; - } + haswell_get_ddi_pll(dev_priv, port, pipe_config); if (pipe_config->shared_dpll >= 0) { pll = &dev_priv->shared_dplls[pipe_config->shared_dpll]; @@ -7719,7 +7895,12 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, pipe_config->ips_enabled = hsw_crtc_supports_ips(crtc) && (I915_READ(IPS_CTL) & IPS_ENABLE); - pipe_config->pixel_multiplier = 1; + if (pipe_config->cpu_transcoder != TRANSCODER_EDP) { + pipe_config->pixel_multiplier = + I915_READ(PIPE_MULT(pipe_config->cpu_transcoder)) + 1; + } else { + pipe_config->pixel_multiplier = 1; + } return true; } @@ -8037,74 +8218,62 @@ static void i845_update_cursor(struct drm_crtc *crtc, u32 base) struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - uint32_t cntl; + uint32_t cntl = 0, size = 0; - if (base != intel_crtc->cursor_base) { - /* On these chipsets we can only modify the base whilst - * the cursor is disabled. - */ - if (intel_crtc->cursor_cntl) { - I915_WRITE(_CURACNTR, 0); - POSTING_READ(_CURACNTR); - intel_crtc->cursor_cntl = 0; + if (base) { + unsigned int width = intel_crtc->cursor_width; + unsigned int height = intel_crtc->cursor_height; + unsigned int stride = roundup_pow_of_two(width) * 4; + + switch (stride) { + default: + WARN_ONCE(1, "Invalid cursor width/stride, width=%u, stride=%u\n", + width, stride); + stride = 256; + /* fallthrough */ + case 256: + case 512: + case 1024: + case 2048: + break; } - I915_WRITE(_CURABASE, base); - POSTING_READ(_CURABASE); + cntl |= CURSOR_ENABLE | + CURSOR_GAMMA_ENABLE | + CURSOR_FORMAT_ARGB | + CURSOR_STRIDE(stride); + + size = (height << 12) | width; } - /* XXX width must be 64, stride 256 => 0x00 << 28 */ - cntl = 0; - if (base) - cntl = (CURSOR_ENABLE | - CURSOR_GAMMA_ENABLE | - CURSOR_FORMAT_ARGB); - if (intel_crtc->cursor_cntl != cntl) { - I915_WRITE(_CURACNTR, cntl); + if (intel_crtc->cursor_cntl != 0 && + (intel_crtc->cursor_base != base || + intel_crtc->cursor_size != size || + intel_crtc->cursor_cntl != cntl)) { + /* On these chipsets we can only modify the base/size/stride + * whilst the cursor is disabled. + */ + I915_WRITE(_CURACNTR, 0); POSTING_READ(_CURACNTR); - intel_crtc->cursor_cntl = cntl; + intel_crtc->cursor_cntl = 0; } -} -static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base) -{ - struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int pipe = intel_crtc->pipe; - uint32_t cntl; + if (intel_crtc->cursor_base != base) + I915_WRITE(_CURABASE, base); - cntl = 0; - if (base) { - cntl = MCURSOR_GAMMA_ENABLE; - switch (intel_crtc->cursor_width) { - case 64: - cntl |= CURSOR_MODE_64_ARGB_AX; - break; - case 128: - cntl |= CURSOR_MODE_128_ARGB_AX; - break; - case 256: - cntl |= CURSOR_MODE_256_ARGB_AX; - break; - default: - WARN_ON(1); - return; - } - cntl |= pipe << 28; /* Connect to correct pipe */ + if (intel_crtc->cursor_size != size) { + I915_WRITE(CURSIZE, size); + intel_crtc->cursor_size = size; } + if (intel_crtc->cursor_cntl != cntl) { - I915_WRITE(CURCNTR(pipe), cntl); - POSTING_READ(CURCNTR(pipe)); + I915_WRITE(_CURACNTR, cntl); + POSTING_READ(_CURACNTR); intel_crtc->cursor_cntl = cntl; } - - /* and commit changes on next vblank */ - I915_WRITE(CURBASE(pipe), base); - POSTING_READ(CURBASE(pipe)); } -static void ivb_update_cursor(struct drm_crtc *crtc, u32 base) +static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -8129,6 +8298,7 @@ static void ivb_update_cursor(struct drm_crtc *crtc, u32 base) WARN_ON(1); return; } + cntl |= pipe << 28; /* Connect to correct pipe */ } if (IS_HASWELL(dev) || IS_BROADWELL(dev)) cntl |= CURSOR_PIPE_CSC_ENABLE; @@ -8188,15 +8358,50 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc, I915_WRITE(CURPOS(pipe), pos); - if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_BROADWELL(dev)) - ivb_update_cursor(crtc, base); - else if (IS_845G(dev) || IS_I865G(dev)) + if (IS_845G(dev) || IS_I865G(dev)) i845_update_cursor(crtc, base); else i9xx_update_cursor(crtc, base); intel_crtc->cursor_base = base; } +static bool cursor_size_ok(struct drm_device *dev, + uint32_t width, uint32_t height) +{ + if (width == 0 || height == 0) + return false; + + /* + * 845g/865g are special in that they are only limited by + * the width of their cursors, the height is arbitrary up to + * the precision of the register. Everything else requires + * square cursors, limited to a few power-of-two sizes. + */ + if (IS_845G(dev) || IS_I865G(dev)) { + if ((width & 63) != 0) + return false; + + if (width > (IS_845G(dev) ? 64 : 512)) + return false; + + if (height > 1023) + return false; + } else { + switch (width | height) { + case 256: + case 128: + if (IS_GEN2(dev)) + return false; + case 64: + break; + default: + return false; + } + } + + return true; +} + /* * intel_crtc_cursor_set_obj - Set cursor to specified GEM object * @@ -8212,7 +8417,7 @@ static int intel_crtc_cursor_set_obj(struct drm_crtc *crtc, struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); enum pipe pipe = intel_crtc->pipe; - unsigned old_width; + unsigned old_width, stride; uint32_t addr; int ret; @@ -8220,20 +8425,18 @@ static int intel_crtc_cursor_set_obj(struct drm_crtc *crtc, if (!obj) { DRM_DEBUG_KMS("cursor off\n"); addr = 0; - obj = NULL; mutex_lock(&dev->struct_mutex); goto finish; } /* Check for which cursor types we support */ - if (!((width == 64 && height == 64) || - (width == 128 && height == 128 && !IS_GEN2(dev)) || - (width == 256 && height == 256 && !IS_GEN2(dev)))) { + if (!cursor_size_ok(dev, width, height)) { DRM_DEBUG("Cursor dimension not supported\n"); return -EINVAL; } - if (obj->base.size < width * height * 4) { + stride = roundup_pow_of_two(width) * 4; + if (obj->base.size < stride * height) { DRM_DEBUG_KMS("buffer is too small\n"); ret = -ENOMEM; goto fail; @@ -8295,9 +8498,6 @@ static int intel_crtc_cursor_set_obj(struct drm_crtc *crtc, addr = obj->phys_handle->busaddr; } - if (IS_GEN2(dev)) - I915_WRITE(CURSIZE, (height << 12) | width); - finish: if (intel_crtc->cursor_bo) { if (!INTEL_INFO(dev)->cursor_needs_physical) @@ -8944,12 +9144,13 @@ static void intel_mark_fb_busy(struct drm_device *dev, unsigned frontbuffer_bits, struct intel_engine_cs *ring) { + struct drm_i915_private *dev_priv = dev->dev_private; enum pipe pipe; if (!i915.powersave) return; - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { if (!(frontbuffer_bits & INTEL_FRONTBUFFER_ALL_MASK(pipe))) continue; @@ -9019,6 +9220,14 @@ void intel_frontbuffer_flush(struct drm_device *dev, intel_mark_fb_busy(dev, frontbuffer_bits, NULL); intel_edp_psr_flush(dev, frontbuffer_bits); + + /* + * FIXME: Unconditional fbc flushing here is a rather gross hack and + * needs to be reworked into a proper frontbuffer tracking scheme like + * psr employs. + */ + if (IS_BROADWELL(dev)) + gen8_fbc_sw_flush(dev, FBC_REND_CACHE_CLEAN); } /** @@ -9151,7 +9360,6 @@ static void intel_unpin_work_fn(struct work_struct *__work) static void do_intel_finish_page_flip(struct drm_device *dev, struct drm_crtc *crtc) { - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_unpin_work *work; unsigned long flags; @@ -9171,23 +9379,9 @@ static void do_intel_finish_page_flip(struct drm_device *dev, return; } - /* and that the unpin work is consistent wrt ->pending. */ - smp_rmb(); - - intel_crtc->unpin_work = NULL; - - if (work->event) - drm_send_vblank_event(dev, intel_crtc->pipe, work->event); - - drm_crtc_vblank_put(crtc); + page_flip_completed(intel_crtc); spin_unlock_irqrestore(&dev->event_lock, flags); - - wake_up_all(&dev_priv->pending_flip_queue); - - queue_work(dev_priv->wq, &work->work); - - trace_i915_flip_complete(intel_crtc->plane, work->pending_flip_obj); } void intel_finish_page_flip(struct drm_device *dev, int pipe) @@ -9532,6 +9726,8 @@ static bool use_mmio_flip(struct intel_engine_cs *ring, return false; else if (i915.use_mmio_flip > 0) return true; + else if (i915.enable_execlists) + return true; else return ring != obj->ring; } @@ -9665,6 +9861,65 @@ static int intel_default_queue_flip(struct drm_device *dev, return -ENODEV; } +static bool __intel_pageflip_stall_check(struct drm_device *dev, + struct drm_crtc *crtc) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_unpin_work *work = intel_crtc->unpin_work; + u32 addr; + + if (atomic_read(&work->pending) >= INTEL_FLIP_COMPLETE) + return true; + + if (!work->enable_stall_check) + return false; + + if (work->flip_ready_vblank == 0) { + if (work->flip_queued_ring && + !i915_seqno_passed(work->flip_queued_ring->get_seqno(work->flip_queued_ring, true), + work->flip_queued_seqno)) + return false; + + work->flip_ready_vblank = drm_vblank_count(dev, intel_crtc->pipe); + } + + if (drm_vblank_count(dev, intel_crtc->pipe) - work->flip_ready_vblank < 3) + return false; + + /* Potential stall - if we see that the flip has happened, + * assume a missed interrupt. */ + if (INTEL_INFO(dev)->gen >= 4) + addr = I915_HI_DISPBASE(I915_READ(DSPSURF(intel_crtc->plane))); + else + addr = I915_READ(DSPADDR(intel_crtc->plane)); + + /* There is a potential issue here with a false positive after a flip + * to the same address. We could address this by checking for a + * non-incrementing frame counter. + */ + return addr == work->gtt_offset; +} + +void intel_check_page_flip(struct drm_device *dev, int pipe) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + unsigned long flags; + + if (crtc == NULL) + return; + + spin_lock_irqsave(&dev->event_lock, flags); + if (intel_crtc->unpin_work && __intel_pageflip_stall_check(dev, crtc)) { + WARN_ONCE(1, "Kicking stuck page flip: queued at %d, now %d\n", + intel_crtc->unpin_work->flip_queued_vblank, drm_vblank_count(dev, pipe)); + page_flip_completed(intel_crtc); + } + spin_unlock_irqrestore(&dev->event_lock, flags); +} + static int intel_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, @@ -9721,12 +9976,20 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, /* We borrow the event spin lock for protecting unpin_work */ spin_lock_irqsave(&dev->event_lock, flags); if (intel_crtc->unpin_work) { - spin_unlock_irqrestore(&dev->event_lock, flags); - kfree(work); - drm_crtc_vblank_put(crtc); + /* Before declaring the flip queue wedged, check if + * the hardware completed the operation behind our backs. + */ + if (__intel_pageflip_stall_check(dev, crtc)) { + DRM_DEBUG_DRIVER("flip queue: previous flip completed, continuing\n"); + page_flip_completed(intel_crtc); + } else { + DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); + spin_unlock_irqrestore(&dev->event_lock, flags); - DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); - return -EBUSY; + drm_crtc_vblank_put(crtc); + kfree(work); + return -EBUSY; + } } intel_crtc->unpin_work = work; spin_unlock_irqrestore(&dev->event_lock, flags); @@ -9746,8 +10009,6 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, work->pending_flip_obj = obj; - work->enable_stall_check = true; - atomic_inc(&intel_crtc->unpin_work_count); intel_crtc->reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter); @@ -9776,14 +10037,26 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, work->gtt_offset = i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset; - if (use_mmio_flip(ring, obj)) + if (use_mmio_flip(ring, obj)) { ret = intel_queue_mmio_flip(dev, crtc, fb, obj, ring, page_flip_flags); - else + if (ret) + goto cleanup_unpin; + + work->flip_queued_seqno = obj->last_write_seqno; + work->flip_queued_ring = obj->ring; + } else { ret = dev_priv->display.queue_flip(dev, crtc, fb, obj, ring, - page_flip_flags); - if (ret) - goto cleanup_unpin; + page_flip_flags); + if (ret) + goto cleanup_unpin; + + work->flip_queued_seqno = intel_ring_get_seqno(ring); + work->flip_queued_ring = ring; + } + + work->flip_queued_vblank = drm_vblank_count(dev, intel_crtc->pipe); + work->enable_stall_check = true; i915_gem_track_fb(work->old_fb_obj, obj, INTEL_FRONTBUFFER_PRIMARY(pipe)); @@ -9818,8 +10091,11 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, out_hang: intel_crtc_wait_for_pending_flips(crtc); ret = intel_pipe_set_base(crtc, crtc->x, crtc->y, fb); - if (ret == 0 && event) + if (ret == 0 && event) { + spin_lock_irqsave(&dev->event_lock, flags); drm_send_vblank_event(dev, pipe, event); + spin_unlock_irqrestore(&dev->event_lock, flags); + } } return ret; } @@ -9847,8 +10123,7 @@ static void intel_modeset_update_staged_output_state(struct drm_device *dev) to_intel_encoder(connector->base.encoder); } - list_for_each_entry(encoder, &dev->mode_config.encoder_list, - base.head) { + for_each_intel_encoder(dev, encoder) { encoder->new_crtc = to_intel_crtc(encoder->base.crtc); } @@ -9879,8 +10154,7 @@ static void intel_modeset_commit_output_state(struct drm_device *dev) connector->base.encoder = &connector->new_encoder->base; } - list_for_each_entry(encoder, &dev->mode_config.encoder_list, - base.head) { + for_each_intel_encoder(dev, encoder) { encoder->base.crtc = &encoder->new_crtc->base; } @@ -10007,6 +10281,15 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc, pipe_config->dp_m_n.gmch_m, pipe_config->dp_m_n.gmch_n, pipe_config->dp_m_n.link_m, pipe_config->dp_m_n.link_n, pipe_config->dp_m_n.tu); + + DRM_DEBUG_KMS("dp: %i, gmch_m2: %u, gmch_n2: %u, link_m2: %u, link_n2: %u, tu2: %u\n", + pipe_config->has_dp_encoder, + pipe_config->dp_m2_n2.gmch_m, + pipe_config->dp_m2_n2.gmch_n, + pipe_config->dp_m2_n2.link_m, + pipe_config->dp_m2_n2.link_n, + pipe_config->dp_m2_n2.tu); + DRM_DEBUG_KMS("requested mode:\n"); drm_mode_debug_printmodeline(&pipe_config->requested_mode); DRM_DEBUG_KMS("adjusted mode:\n"); @@ -10041,8 +10324,7 @@ static bool check_single_encoder_cloning(struct intel_crtc *crtc, struct drm_device *dev = crtc->base.dev; struct intel_encoder *source_encoder; - list_for_each_entry(source_encoder, - &dev->mode_config.encoder_list, base.head) { + for_each_intel_encoder(dev, source_encoder) { if (source_encoder->new_crtc != crtc) continue; @@ -10058,8 +10340,7 @@ static bool check_encoder_cloning(struct intel_crtc *crtc) struct drm_device *dev = crtc->base.dev; struct intel_encoder *encoder; - list_for_each_entry(encoder, - &dev->mode_config.encoder_list, base.head) { + for_each_intel_encoder(dev, encoder) { if (encoder->new_crtc != crtc) continue; @@ -10143,8 +10424,7 @@ intel_modeset_pipe_config(struct drm_crtc *crtc, * adjust it according to limitations or connector properties, and also * a chance to reject the mode entirely. */ - list_for_each_entry(encoder, &dev->mode_config.encoder_list, - base.head) { + for_each_intel_encoder(dev, encoder) { if (&encoder->new_crtc->base != crtc) continue; @@ -10222,8 +10502,7 @@ intel_modeset_affected_pipes(struct drm_crtc *crtc, unsigned *modeset_pipes, 1 << connector->new_encoder->new_crtc->pipe; } - list_for_each_entry(encoder, &dev->mode_config.encoder_list, - base.head) { + for_each_intel_encoder(dev, encoder) { if (encoder->base.crtc == &encoder->new_crtc->base) continue; @@ -10297,8 +10576,7 @@ intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes) struct intel_crtc *intel_crtc; struct drm_connector *connector; - list_for_each_entry(intel_encoder, &dev->mode_config.encoder_list, - base.head) { + for_each_intel_encoder(dev, intel_encoder) { if (!intel_encoder->base.crtc) continue; @@ -10387,6 +10665,22 @@ intel_pipe_config_compare(struct drm_device *dev, return false; \ } +/* This is required for BDW+ where there is only one set of registers for + * switching between high and low RR. + * This macro can be used whenever a comparison has to be made between one + * hw state and multiple sw state variables. + */ +#define PIPE_CONF_CHECK_I_ALT(name, alt_name) \ + if ((current_config->name != pipe_config->name) && \ + (current_config->alt_name != pipe_config->name)) { \ + DRM_ERROR("mismatch in " #name " " \ + "(expected %i or %i, found %i)\n", \ + current_config->name, \ + current_config->alt_name, \ + pipe_config->name); \ + return false; \ + } + #define PIPE_CONF_CHECK_FLAGS(name, mask) \ if ((current_config->name ^ pipe_config->name) & (mask)) { \ DRM_ERROR("mismatch in " #name "(" #mask ") " \ @@ -10419,11 +10713,28 @@ intel_pipe_config_compare(struct drm_device *dev, PIPE_CONF_CHECK_I(fdi_m_n.tu); PIPE_CONF_CHECK_I(has_dp_encoder); - PIPE_CONF_CHECK_I(dp_m_n.gmch_m); - PIPE_CONF_CHECK_I(dp_m_n.gmch_n); - PIPE_CONF_CHECK_I(dp_m_n.link_m); - PIPE_CONF_CHECK_I(dp_m_n.link_n); - PIPE_CONF_CHECK_I(dp_m_n.tu); + + if (INTEL_INFO(dev)->gen < 8) { + PIPE_CONF_CHECK_I(dp_m_n.gmch_m); + PIPE_CONF_CHECK_I(dp_m_n.gmch_n); + PIPE_CONF_CHECK_I(dp_m_n.link_m); + PIPE_CONF_CHECK_I(dp_m_n.link_n); + PIPE_CONF_CHECK_I(dp_m_n.tu); + + if (current_config->has_drrs) { + PIPE_CONF_CHECK_I(dp_m2_n2.gmch_m); + PIPE_CONF_CHECK_I(dp_m2_n2.gmch_n); + PIPE_CONF_CHECK_I(dp_m2_n2.link_m); + PIPE_CONF_CHECK_I(dp_m2_n2.link_n); + PIPE_CONF_CHECK_I(dp_m2_n2.tu); + } + } else { + PIPE_CONF_CHECK_I_ALT(dp_m_n.gmch_m, dp_m2_n2.gmch_m); + PIPE_CONF_CHECK_I_ALT(dp_m_n.gmch_n, dp_m2_n2.gmch_n); + PIPE_CONF_CHECK_I_ALT(dp_m_n.link_m, dp_m2_n2.link_m); + PIPE_CONF_CHECK_I_ALT(dp_m_n.link_n, dp_m2_n2.link_n); + PIPE_CONF_CHECK_I_ALT(dp_m_n.tu, dp_m2_n2.tu); + } PIPE_CONF_CHECK_I(adjusted_mode.crtc_hdisplay); PIPE_CONF_CHECK_I(adjusted_mode.crtc_htotal); @@ -10509,6 +10820,7 @@ intel_pipe_config_compare(struct drm_device *dev, #undef PIPE_CONF_CHECK_X #undef PIPE_CONF_CHECK_I +#undef PIPE_CONF_CHECK_I_ALT #undef PIPE_CONF_CHECK_FLAGS #undef PIPE_CONF_CHECK_CLOCK_FUZZY #undef PIPE_CONF_QUIRK @@ -10538,8 +10850,7 @@ check_encoder_state(struct drm_device *dev) struct intel_encoder *encoder; struct intel_connector *connector; - list_for_each_entry(encoder, &dev->mode_config.encoder_list, - base.head) { + for_each_intel_encoder(dev, encoder) { bool enabled = false; bool active = false; enum pipe pipe, tracked_pipe; @@ -10618,8 +10929,7 @@ check_crtc_state(struct drm_device *dev) WARN(crtc->active && !crtc->base.enabled, "active crtc, but not enabled in sw tracking\n"); - list_for_each_entry(encoder, &dev->mode_config.encoder_list, - base.head) { + for_each_intel_encoder(dev, encoder) { if (encoder->base.crtc != &crtc->base) continue; enabled = true; @@ -10637,12 +10947,12 @@ check_crtc_state(struct drm_device *dev) active = dev_priv->display.get_pipe_config(crtc, &pipe_config); - /* hw state is inconsistent with the pipe A quirk */ - if (crtc->pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) + /* hw state is inconsistent with the pipe quirk */ + if ((crtc->pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) || + (crtc->pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE)) active = crtc->active; - list_for_each_entry(encoder, &dev->mode_config.encoder_list, - base.head) { + for_each_intel_encoder(dev, encoder) { enum pipe pipe; if (encoder->base.crtc != &crtc->base) continue; @@ -11010,7 +11320,7 @@ static void intel_set_config_restore_state(struct drm_device *dev, } count = 0; - list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { + for_each_intel_encoder(dev, encoder) { encoder->new_crtc = to_intel_crtc(config->save_encoder_crtcs[count++]); } @@ -11169,8 +11479,7 @@ intel_modeset_stage_output_state(struct drm_device *dev, } /* Check for any encoders that needs to be disabled. */ - list_for_each_entry(encoder, &dev->mode_config.encoder_list, - base.head) { + for_each_intel_encoder(dev, encoder) { int num_connectors = 0; list_for_each_entry(connector, &dev->mode_config.connector_list, @@ -11203,9 +11512,7 @@ intel_modeset_stage_output_state(struct drm_device *dev, for_each_intel_crtc(dev, crtc) { crtc->new_enabled = false; - list_for_each_entry(encoder, - &dev->mode_config.encoder_list, - base.head) { + for_each_intel_encoder(dev, encoder) { if (encoder->new_crtc == crtc) { crtc->new_enabled = true; break; @@ -11242,7 +11549,7 @@ static void disable_crtc_nofb(struct intel_crtc *crtc) connector->new_encoder = NULL; } - list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { + for_each_intel_encoder(dev, encoder) { if (encoder->new_crtc == crtc) encoder->new_crtc = NULL; } @@ -11305,7 +11612,6 @@ static int intel_crtc_set_config(struct drm_mode_set *set) ret = intel_set_mode(set->crtc, set->mode, set->x, set->y, set->fb); } else if (config->fb_changed) { - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(set->crtc); intel_crtc_wait_for_pending_flips(set->crtc); @@ -11319,8 +11625,7 @@ static int intel_crtc_set_config(struct drm_mode_set *set) */ if (!intel_crtc->primary_enabled && ret == 0) { WARN_ON(!intel_crtc->active); - intel_enable_primary_hw_plane(dev_priv, intel_crtc->plane, - intel_crtc->pipe); + intel_enable_primary_hw_plane(set->crtc->primary, set->crtc); } /* @@ -11473,8 +11778,6 @@ static int intel_primary_plane_disable(struct drm_plane *plane) { struct drm_device *dev = plane->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_plane *intel_plane = to_intel_plane(plane); struct intel_crtc *intel_crtc; if (!plane->fb) @@ -11497,8 +11800,8 @@ intel_primary_plane_disable(struct drm_plane *plane) goto disable_unpin; intel_crtc_wait_for_pending_flips(plane->crtc); - intel_disable_primary_hw_plane(dev_priv, intel_plane->plane, - intel_plane->pipe); + intel_disable_primary_hw_plane(plane, plane->crtc); + disable_unpin: mutex_lock(&dev->struct_mutex); i915_gem_track_fb(intel_fb_obj(plane->fb), NULL, @@ -11520,7 +11823,6 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_plane *intel_plane = to_intel_plane(plane); struct drm_i915_gem_object *obj = intel_fb_obj(fb); struct drm_i915_gem_object *old_obj = intel_fb_obj(plane->fb); struct drm_rect dest = { @@ -11542,6 +11844,21 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, .x2 = intel_crtc->active ? intel_crtc->config.pipe_src_w : 0, .y2 = intel_crtc->active ? intel_crtc->config.pipe_src_h : 0, }; + const struct { + int crtc_x, crtc_y; + unsigned int crtc_w, crtc_h; + uint32_t src_x, src_y, src_w, src_h; + } orig = { + .crtc_x = crtc_x, + .crtc_y = crtc_y, + .crtc_w = crtc_w, + .crtc_h = crtc_h, + .src_x = src_x, + .src_y = src_y, + .src_w = src_w, + .src_h = src_h, + }; + struct intel_plane *intel_plane = to_intel_plane(plane); bool visible; int ret; @@ -11607,9 +11924,7 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe)); if (intel_crtc->primary_enabled) - intel_disable_primary_hw_plane(dev_priv, - intel_plane->plane, - intel_plane->pipe); + intel_disable_primary_hw_plane(plane, crtc); if (plane->fb != fb) @@ -11618,16 +11933,42 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc, mutex_unlock(&dev->struct_mutex); - return 0; - } + } else { + if (intel_crtc && intel_crtc->active && + intel_crtc->primary_enabled) { + /* + * FBC does not work on some platforms for rotated + * planes, so disable it when rotation is not 0 and + * update it when rotation is set back to 0. + * + * FIXME: This is redundant with the fbc update done in + * the primary plane enable function except that that + * one is done too late. We eventually need to unify + * this. + */ + if (INTEL_INFO(dev)->gen <= 4 && !IS_G4X(dev) && + dev_priv->fbc.plane == intel_crtc->plane && + intel_plane->rotation != BIT(DRM_ROTATE_0)) { + intel_disable_fbc(dev); + } + } + ret = intel_pipe_set_base(crtc, src.x1, src.y1, fb); + if (ret) + return ret; - ret = intel_pipe_set_base(crtc, src.x1, src.y1, fb); - if (ret) - return ret; + if (!intel_crtc->primary_enabled) + intel_enable_primary_hw_plane(plane, crtc); + } - if (!intel_crtc->primary_enabled) - intel_enable_primary_hw_plane(dev_priv, intel_crtc->plane, - intel_crtc->pipe); + intel_plane->crtc_x = orig.crtc_x; + intel_plane->crtc_y = orig.crtc_y; + intel_plane->crtc_w = orig.crtc_w; + intel_plane->crtc_h = orig.crtc_h; + intel_plane->src_x = orig.src_x; + intel_plane->src_y = orig.src_y; + intel_plane->src_w = orig.src_w; + intel_plane->src_h = orig.src_h; + intel_plane->obj = obj; return 0; } @@ -11644,6 +11985,7 @@ static const struct drm_plane_funcs intel_primary_plane_funcs = { .update_plane = intel_primary_plane_setplane, .disable_plane = intel_primary_plane_disable, .destroy = intel_plane_destroy, + .set_property = intel_plane_set_property }; static struct drm_plane *intel_primary_plane_create(struct drm_device *dev, @@ -11661,6 +12003,7 @@ static struct drm_plane *intel_primary_plane_create(struct drm_device *dev, primary->max_downscale = 1; primary->pipe = pipe; primary->plane = pipe; + primary->rotation = BIT(DRM_ROTATE_0); if (HAS_FBC(dev) && INTEL_INFO(dev)->gen < 4) primary->plane = !pipe; @@ -11676,6 +12019,19 @@ static struct drm_plane *intel_primary_plane_create(struct drm_device *dev, &intel_primary_plane_funcs, intel_primary_formats, num_formats, DRM_PLANE_TYPE_PRIMARY); + + if (INTEL_INFO(dev)->gen >= 4) { + if (!dev->mode_config.rotation_property) + dev->mode_config.rotation_property = + drm_mode_create_rotation_property(dev, + BIT(DRM_ROTATE_0) | + BIT(DRM_ROTATE_180)); + if (dev->mode_config.rotation_property) + drm_object_attach_property(&primary->base.base, + dev->mode_config.rotation_property, + primary->rotation); + } + return &primary->base; } @@ -11736,6 +12092,10 @@ intel_cursor_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, return intel_crtc_cursor_set_obj(crtc, obj, crtc_w, crtc_h); } else { intel_crtc_update_cursor(crtc, visible); + + intel_frontbuffer_flip(crtc->dev, + INTEL_FRONTBUFFER_CURSOR(intel_crtc->pipe)); + return 0; } } @@ -11812,8 +12172,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) intel_crtc->cursor_base = ~0; intel_crtc->cursor_cntl = ~0; - - init_waitqueue_head(&intel_crtc->vbl_wait); + intel_crtc->cursor_size = ~0; BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) || dev_priv->plane_to_crtc_mapping[intel_crtc->plane] != NULL); @@ -11876,8 +12235,7 @@ static int intel_encoder_clones(struct intel_encoder *encoder) int index_mask = 0; int entry = 0; - list_for_each_entry(source_encoder, - &dev->mode_config.encoder_list, base.head) { + for_each_intel_encoder(dev, source_encoder) { if (encoders_cloneable(encoder, source_encoder)) index_mask |= (1 << entry); @@ -12066,7 +12424,7 @@ static void intel_setup_outputs(struct drm_device *dev) intel_edp_psr_init(dev); - list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { + for_each_intel_encoder(dev, encoder) { encoder->base.possible_crtcs = encoder->crtc_mask; encoder->base.possible_clones = intel_encoder_clones(encoder); @@ -12332,29 +12690,27 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.get_display_clock_speed = i830_get_display_clock_speed; - if (HAS_PCH_SPLIT(dev)) { - if (IS_GEN5(dev)) { - dev_priv->display.fdi_link_train = ironlake_fdi_link_train; - dev_priv->display.write_eld = ironlake_write_eld; - } else if (IS_GEN6(dev)) { - dev_priv->display.fdi_link_train = gen6_fdi_link_train; - dev_priv->display.write_eld = ironlake_write_eld; - dev_priv->display.modeset_global_resources = - snb_modeset_global_resources; - } else if (IS_IVYBRIDGE(dev)) { - /* FIXME: detect B0+ stepping and use auto training */ - dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train; - dev_priv->display.write_eld = ironlake_write_eld; - dev_priv->display.modeset_global_resources = - ivb_modeset_global_resources; - } else if (IS_HASWELL(dev) || IS_GEN8(dev)) { - dev_priv->display.fdi_link_train = hsw_fdi_link_train; - dev_priv->display.write_eld = haswell_write_eld; - dev_priv->display.modeset_global_resources = - haswell_modeset_global_resources; - } - } else if (IS_G4X(dev)) { + if (IS_G4X(dev)) { dev_priv->display.write_eld = g4x_write_eld; + } else if (IS_GEN5(dev)) { + dev_priv->display.fdi_link_train = ironlake_fdi_link_train; + dev_priv->display.write_eld = ironlake_write_eld; + } else if (IS_GEN6(dev)) { + dev_priv->display.fdi_link_train = gen6_fdi_link_train; + dev_priv->display.write_eld = ironlake_write_eld; + dev_priv->display.modeset_global_resources = + snb_modeset_global_resources; + } else if (IS_IVYBRIDGE(dev)) { + /* FIXME: detect B0+ stepping and use auto training */ + dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train; + dev_priv->display.write_eld = ironlake_write_eld; + dev_priv->display.modeset_global_resources = + ivb_modeset_global_resources; + } else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { + dev_priv->display.fdi_link_train = hsw_fdi_link_train; + dev_priv->display.write_eld = haswell_write_eld; + dev_priv->display.modeset_global_resources = + haswell_modeset_global_resources; } else if (IS_VALLEYVIEW(dev)) { dev_priv->display.modeset_global_resources = valleyview_modeset_global_resources; @@ -12388,6 +12744,8 @@ static void intel_init_display(struct drm_device *dev) } intel_panel_init_backlight_funcs(dev); + + mutex_init(&dev_priv->pps_mutex); } /* @@ -12403,6 +12761,14 @@ static void quirk_pipea_force(struct drm_device *dev) DRM_INFO("applying pipe a force quirk\n"); } +static void quirk_pipeb_force(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + dev_priv->quirks |= QUIRK_PIPEB_FORCE; + DRM_INFO("applying pipe b force quirk\n"); +} + /* * Some machines (Lenovo U160) do not work with SSC on LVDS for some reason */ @@ -12477,6 +12843,12 @@ static struct intel_quirk intel_quirks[] = { /* ThinkPad T60 needs pipe A force quirk (bug #16494) */ { 0x2782, 0x17aa, 0x201a, quirk_pipea_force }, + /* 830 needs to leave pipe A & dpll A up */ + { 0x3577, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force }, + + /* 830 needs to leave pipe B & dpll B up */ + { 0x3577, PCI_ANY_ID, PCI_ANY_ID, quirk_pipeb_force }, + /* Lenovo U160 cannot use SSC on LVDS */ { 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable }, @@ -12550,7 +12922,11 @@ static void i915_disable_vga(struct drm_device *dev) vga_put(dev->pdev, VGA_RSRC_LEGACY_IO); udelay(300); - I915_WRITE(vga_reg, VGA_DISP_DISABLE); + /* + * Fujitsu-Siemens Lifebook S6010 (830) has problems resuming + * from S3 without preserving (some of?) the other bits. + */ + I915_WRITE(vga_reg, dev_priv->bios_vgacntr | VGA_DISP_DISABLE); POSTING_READ(vga_reg); } @@ -12563,8 +12939,6 @@ void intel_modeset_init_hw(struct drm_device *dev) intel_init_clock_gating(dev); - intel_reset_dpio(dev); - intel_enable_gt_powersave(dev); } @@ -12610,7 +12984,10 @@ void intel_modeset_init(struct drm_device *dev) dev->mode_config.max_height = 8192; } - if (IS_GEN2(dev)) { + if (IS_845G(dev) || IS_I865G(dev)) { + dev->mode_config.cursor_width = IS_845G(dev) ? 64 : 512; + dev->mode_config.cursor_height = 1023; + } else if (IS_GEN2(dev)) { dev->mode_config.cursor_width = GEN2_CURSOR_WIDTH; dev->mode_config.cursor_height = GEN2_CURSOR_HEIGHT; } else { @@ -12624,7 +13001,7 @@ void intel_modeset_init(struct drm_device *dev) INTEL_INFO(dev)->num_pipes, INTEL_INFO(dev)->num_pipes > 1 ? "s" : ""); - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { intel_crtc_init(dev, pipe); for_each_sprite(pipe, sprite) { ret = intel_plane_init(dev, pipe, sprite); @@ -12635,10 +13012,11 @@ void intel_modeset_init(struct drm_device *dev) } intel_init_dpio(dev); - intel_reset_dpio(dev); intel_shared_dpll_init(dev); + /* save the BIOS value before clobbering it */ + dev_priv->bios_vgacntr = I915_READ(i915_vgacntrl_reg(dev)); /* Just disable it once at startup */ i915_disable_vga(dev); intel_setup_outputs(dev); @@ -12730,9 +13108,10 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) I915_WRITE(reg, I915_READ(reg) & ~PIPECONF_FRAME_START_DELAY_MASK); /* restore vblank interrupts to correct state */ - if (crtc->active) + if (crtc->active) { + update_scanline_offset(crtc); drm_vblank_on(dev, crtc->pipe); - else + } else drm_vblank_off(dev, crtc->pipe); /* We need to sanitize the plane -> pipe mapping first because this will @@ -12815,7 +13194,7 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) } } - if (crtc->active || IS_VALLEYVIEW(dev) || INTEL_INFO(dev)->gen < 5) { + if (crtc->active || HAS_GMCH_DISPLAY(dev)) { /* * We start out with underrun reporting disabled to avoid races. * For correct bookkeeping mark this on active crtcs. @@ -12831,8 +13210,6 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) */ crtc->cpu_fifo_underrun_disabled = true; crtc->pch_fifo_underrun_disabled = true; - - update_scanline_offset(crtc); } } @@ -12964,8 +13341,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) intel_display_power_get(dev_priv, POWER_DOMAIN_PLLS); } - list_for_each_entry(encoder, &dev->mode_config.encoder_list, - base.head) { + for_each_intel_encoder(dev, encoder) { pipe = 0; if (encoder->get_hw_state(encoder, &pipe)) { @@ -13029,12 +13405,11 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, } /* HW state is read out, now we need to sanitize this mess. */ - list_for_each_entry(encoder, &dev->mode_config.encoder_list, - base.head) { + for_each_intel_encoder(dev, encoder) { intel_sanitize_encoder(encoder); } - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); intel_sanitize_crtc(crtc); intel_dump_pipe_config(crtc, &crtc->config, "[setup_hw_state]"); @@ -13062,7 +13437,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, * We need to use raw interfaces for restoring state to avoid * checking (bogus) intermediate states. */ - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; @@ -13283,7 +13658,7 @@ intel_display_capture_error_state(struct drm_device *dev) if (IS_HASWELL(dev) || IS_BROADWELL(dev)) error->power_well_driver = I915_READ(HSW_PWR_WELL_DRIVER); - for_each_pipe(i) { + for_each_pipe(dev_priv, i) { error->pipe[i].power_domain_on = intel_display_power_enabled_unlocked(dev_priv, POWER_DOMAIN_PIPE(i)); @@ -13347,6 +13722,7 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m, struct drm_device *dev, struct intel_display_error_state *error) { + struct drm_i915_private *dev_priv = dev->dev_private; int i; if (!error) @@ -13356,7 +13732,7 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m, if (IS_HASWELL(dev) || IS_BROADWELL(dev)) err_printf(m, "PWR_WELL_CTL2: %08x\n", error->power_well_driver); - for_each_pipe(i) { + for_each_pipe(dev_priv, i) { err_printf(m, "Pipe [%d]:\n", i); err_printf(m, " Power: %s\n", error->pipe[i].power_domain_on ? "on" : "off"); @@ -13397,3 +13773,25 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m, err_printf(m, " VSYNC: %08x\n", error->transcoder[i].vsync); } } + +void intel_modeset_preclose(struct drm_device *dev, struct drm_file *file) +{ + struct intel_crtc *crtc; + + for_each_intel_crtc(dev, crtc) { + struct intel_unpin_work *work; + unsigned long irqflags; + + spin_lock_irqsave(&dev->event_lock, irqflags); + + work = crtc->unpin_work; + + if (work && work->event && + work->event->base.file_priv == file) { + kfree(work->event); + work->event = NULL; + } + + spin_unlock_irqrestore(&dev->event_lock, irqflags); + } +} diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index fdff1d420c14ab3536717607975c6cfa273fb474..f6a3fdd5589e33420e2f9b15ef4de42193eea356 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -111,7 +111,7 @@ static struct intel_dp *intel_attached_dp(struct drm_connector *connector) } static void intel_dp_link_down(struct intel_dp *intel_dp); -static bool _edp_panel_vdd_on(struct intel_dp *intel_dp); +static bool edp_panel_vdd_on(struct intel_dp *intel_dp); static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync); int @@ -290,32 +290,201 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, struct intel_dp *intel_dp, struct edp_power_seq *out); +static void pps_lock(struct intel_dp *intel_dp) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct intel_encoder *encoder = &intel_dig_port->base; + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum intel_display_power_domain power_domain; + + /* + * See vlv_power_sequencer_reset() why we need + * a power domain reference here. + */ + power_domain = intel_display_port_power_domain(encoder); + intel_display_power_get(dev_priv, power_domain); + + mutex_lock(&dev_priv->pps_mutex); +} + +static void pps_unlock(struct intel_dp *intel_dp) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct intel_encoder *encoder = &intel_dig_port->base; + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum intel_display_power_domain power_domain; + + mutex_unlock(&dev_priv->pps_mutex); + + power_domain = intel_display_port_power_domain(encoder); + intel_display_power_put(dev_priv, power_domain); +} + static enum pipe vlv_power_sequencer_pipe(struct intel_dp *intel_dp) { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); - struct drm_crtc *crtc = intel_dig_port->base.base.crtc; struct drm_device *dev = intel_dig_port->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - enum port port = intel_dig_port->port; - enum pipe pipe; + struct intel_encoder *encoder; + unsigned int pipes = (1 << PIPE_A) | (1 << PIPE_B); + struct edp_power_seq power_seq; + + lockdep_assert_held(&dev_priv->pps_mutex); - /* modeset should have pipe */ - if (crtc) - return to_intel_crtc(crtc)->pipe; + if (intel_dp->pps_pipe != INVALID_PIPE) + return intel_dp->pps_pipe; + + /* + * We don't have power sequencer currently. + * Pick one that's not used by other ports. + */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + struct intel_dp *tmp; + + if (encoder->type != INTEL_OUTPUT_EDP) + continue; + + tmp = enc_to_intel_dp(&encoder->base); + + if (tmp->pps_pipe != INVALID_PIPE) + pipes &= ~(1 << tmp->pps_pipe); + } + + /* + * Didn't find one. This should not happen since there + * are two power sequencers and up to two eDP ports. + */ + if (WARN_ON(pipes == 0)) + return PIPE_A; + + intel_dp->pps_pipe = ffs(pipes) - 1; + + DRM_DEBUG_KMS("picked pipe %c power sequencer for port %c\n", + pipe_name(intel_dp->pps_pipe), + port_name(intel_dig_port->port)); + + /* init power sequencer on this pipe and port */ + intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); + intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, + &power_seq); + + return intel_dp->pps_pipe; +} + +typedef bool (*vlv_pipe_check)(struct drm_i915_private *dev_priv, + enum pipe pipe); + +static bool vlv_pipe_has_pp_on(struct drm_i915_private *dev_priv, + enum pipe pipe) +{ + return I915_READ(VLV_PIPE_PP_STATUS(pipe)) & PP_ON; +} + +static bool vlv_pipe_has_vdd_on(struct drm_i915_private *dev_priv, + enum pipe pipe) +{ + return I915_READ(VLV_PIPE_PP_CONTROL(pipe)) & EDP_FORCE_VDD; +} + +static bool vlv_pipe_any(struct drm_i915_private *dev_priv, + enum pipe pipe) +{ + return true; +} + +static enum pipe +vlv_initial_pps_pipe(struct drm_i915_private *dev_priv, + enum port port, + vlv_pipe_check pipe_check) +{ + enum pipe pipe; - /* init time, try to find a pipe with this port selected */ for (pipe = PIPE_A; pipe <= PIPE_B; pipe++) { u32 port_sel = I915_READ(VLV_PIPE_PP_ON_DELAYS(pipe)) & PANEL_PORT_SELECT_MASK; - if (port_sel == PANEL_PORT_SELECT_DPB_VLV && port == PORT_B) - return pipe; - if (port_sel == PANEL_PORT_SELECT_DPC_VLV && port == PORT_C) - return pipe; + + if (port_sel != PANEL_PORT_SELECT_VLV(port)) + continue; + + if (!pipe_check(dev_priv, pipe)) + continue; + + return pipe; } - /* shrug */ - return PIPE_A; + return INVALID_PIPE; +} + +static void +vlv_initial_power_sequencer_setup(struct intel_dp *intel_dp) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct edp_power_seq power_seq; + enum port port = intel_dig_port->port; + + lockdep_assert_held(&dev_priv->pps_mutex); + + /* try to find a pipe with this port selected */ + /* first pick one where the panel is on */ + intel_dp->pps_pipe = vlv_initial_pps_pipe(dev_priv, port, + vlv_pipe_has_pp_on); + /* didn't find one? pick one where vdd is on */ + if (intel_dp->pps_pipe == INVALID_PIPE) + intel_dp->pps_pipe = vlv_initial_pps_pipe(dev_priv, port, + vlv_pipe_has_vdd_on); + /* didn't find one? pick one with just the correct port */ + if (intel_dp->pps_pipe == INVALID_PIPE) + intel_dp->pps_pipe = vlv_initial_pps_pipe(dev_priv, port, + vlv_pipe_any); + + /* didn't find one? just let vlv_power_sequencer_pipe() pick one when needed */ + if (intel_dp->pps_pipe == INVALID_PIPE) { + DRM_DEBUG_KMS("no initial power sequencer for port %c\n", + port_name(port)); + return; + } + + DRM_DEBUG_KMS("initial power sequencer for port %c: pipe %c\n", + port_name(port), pipe_name(intel_dp->pps_pipe)); + + intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); + intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, + &power_seq); +} + +void vlv_power_sequencer_reset(struct drm_i915_private *dev_priv) +{ + struct drm_device *dev = dev_priv->dev; + struct intel_encoder *encoder; + + if (WARN_ON(!IS_VALLEYVIEW(dev))) + return; + + /* + * We can't grab pps_mutex here due to deadlock with power_domain + * mutex when power_domain functions are called while holding pps_mutex. + * That also means that in order to use pps_pipe the code needs to + * hold both a power domain reference and pps_mutex, and the power domain + * reference get/put must be done while _not_ holding pps_mutex. + * pps_{lock,unlock}() do these steps in the correct order, so one + * should use them always. + */ + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { + struct intel_dp *intel_dp; + + if (encoder->type != INTEL_OUTPUT_EDP) + continue; + + intel_dp = enc_to_intel_dp(&encoder->base); + intel_dp->pps_pipe = INVALID_PIPE; + } } static u32 _pp_ctrl_reg(struct intel_dp *intel_dp) @@ -349,12 +518,15 @@ static int edp_notify_handler(struct notifier_block *this, unsigned long code, struct drm_i915_private *dev_priv = dev->dev_private; u32 pp_div; u32 pp_ctrl_reg, pp_div_reg; - enum pipe pipe = vlv_power_sequencer_pipe(intel_dp); if (!is_edp(intel_dp) || code != SYS_RESTART) return 0; + pps_lock(intel_dp); + if (IS_VALLEYVIEW(dev)) { + enum pipe pipe = vlv_power_sequencer_pipe(intel_dp); + pp_ctrl_reg = VLV_PIPE_PP_CONTROL(pipe); pp_div_reg = VLV_PIPE_PP_DIVISOR(pipe); pp_div = I915_READ(pp_div_reg); @@ -366,6 +538,8 @@ static int edp_notify_handler(struct notifier_block *this, unsigned long code, msleep(intel_dp->panel_power_cycle_delay); } + pps_unlock(intel_dp); + return 0; } @@ -374,6 +548,8 @@ static bool edp_have_panel_power(struct intel_dp *intel_dp) struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; + lockdep_assert_held(&dev_priv->pps_mutex); + return (I915_READ(_pp_stat_reg(intel_dp)) & PP_ON) != 0; } @@ -381,13 +557,10 @@ static bool edp_have_panel_vdd(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); - struct intel_encoder *intel_encoder = &intel_dig_port->base; - enum intel_display_power_domain power_domain; - power_domain = intel_display_port_power_domain(intel_encoder); - return intel_display_power_enabled(dev_priv, power_domain) && - (I915_READ(_pp_ctrl_reg(intel_dp)) & EDP_FORCE_VDD) != 0; + lockdep_assert_held(&dev_priv->pps_mutex); + + return I915_READ(_pp_ctrl_reg(intel_dp)) & EDP_FORCE_VDD; } static void @@ -535,7 +708,15 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, bool has_aux_irq = HAS_AUX_IRQ(dev); bool vdd; - vdd = _edp_panel_vdd_on(intel_dp); + pps_lock(intel_dp); + + /* + * We will be called with VDD already enabled for dpcd/edid/oui reads. + * In such cases we want to leave VDD enabled and it's up to upper layers + * to turn it off. But for eg. i2c-dev access we need to turn it on/off + * ourselves. + */ + vdd = edp_panel_vdd_on(intel_dp); /* dp aux is extremely sensitive to irq latency, hence request the * lowest possible wakeup latency and so prevent the cpu from going into @@ -644,6 +825,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, if (vdd) edp_panel_vdd_off(intel_dp, false); + pps_unlock(intel_dp); + return ret; } @@ -828,20 +1011,6 @@ intel_dp_set_clock(struct intel_encoder *encoder, } } -static void -intel_dp_set_m2_n2(struct intel_crtc *crtc, struct intel_link_m_n *m_n) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - enum transcoder transcoder = crtc->config.cpu_transcoder; - - I915_WRITE(PIPE_DATA_M2(transcoder), - TU_SIZE(m_n->tu) | m_n->gmch_m); - I915_WRITE(PIPE_DATA_N2(transcoder), m_n->gmch_n); - I915_WRITE(PIPE_LINK_M2(transcoder), m_n->link_m); - I915_WRITE(PIPE_LINK_N2(transcoder), m_n->link_n); -} - bool intel_dp_compute_config(struct intel_encoder *encoder, struct intel_crtc_config *pipe_config) @@ -867,6 +1036,7 @@ intel_dp_compute_config(struct intel_encoder *encoder, pipe_config->has_pch_encoder = true; pipe_config->has_dp_encoder = true; + pipe_config->has_drrs = false; pipe_config->has_audio = intel_dp->has_audio; if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) { @@ -898,23 +1068,15 @@ intel_dp_compute_config(struct intel_encoder *encoder, bpp = dev_priv->vbt.edp_bpp; } - if (IS_BROADWELL(dev)) { - /* Yes, it's an ugly hack. */ - min_lane_count = max_lane_count; - DRM_DEBUG_KMS("forcing lane count to max (%u) on BDW\n", - min_lane_count); - } else if (dev_priv->vbt.edp_lanes) { - min_lane_count = min(dev_priv->vbt.edp_lanes, - max_lane_count); - DRM_DEBUG_KMS("using min %u lanes per VBT\n", - min_lane_count); - } - - if (dev_priv->vbt.edp_rate) { - min_clock = min(dev_priv->vbt.edp_rate >> 3, max_clock); - DRM_DEBUG_KMS("using min %02x link bw per VBT\n", - bws[min_clock]); - } + /* + * Use the maximum clock and number of lanes the eDP panel + * advertizes being capable of. The panels are generally + * designed to support only a single clock and lane + * configuration, and typically these values correspond to the + * native resolution of the panel. + */ + min_lane_count = max_lane_count; + min_clock = max_clock; } for (; bpp >= 6*3; bpp -= 2*3) { @@ -970,13 +1132,14 @@ intel_dp_compute_config(struct intel_encoder *encoder, if (intel_connector->panel.downclock_mode != NULL && intel_dp->drrs_state.type == SEAMLESS_DRRS_SUPPORT) { + pipe_config->has_drrs = true; intel_link_compute_m_n(bpp, lane_count, intel_connector->panel.downclock_mode->clock, pipe_config->port_clock, &pipe_config->dp_m2_n2); } - if (HAS_DDI(dev)) + if (IS_HASWELL(dev) || IS_BROADWELL(dev)) hsw_dp_set_ddi_pll_sel(pipe_config, intel_dp->link_bw); else intel_dp_set_clock(encoder, pipe_config, intel_dp->link_bw); @@ -1110,6 +1273,8 @@ static void wait_panel_status(struct intel_dp *intel_dp, struct drm_i915_private *dev_priv = dev->dev_private; u32 pp_stat_reg, pp_ctrl_reg; + lockdep_assert_held(&dev_priv->pps_mutex); + pp_stat_reg = _pp_stat_reg(intel_dp); pp_ctrl_reg = _pp_ctrl_reg(intel_dp); @@ -1173,13 +1338,20 @@ static u32 ironlake_get_pp_control(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = dev->dev_private; u32 control; + lockdep_assert_held(&dev_priv->pps_mutex); + control = I915_READ(_pp_ctrl_reg(intel_dp)); control &= ~PANEL_UNLOCK_MASK; control |= PANEL_UNLOCK_REGS; return control; } -static bool _edp_panel_vdd_on(struct intel_dp *intel_dp) +/* + * Must be paired with edp_panel_vdd_off(). + * Must hold pps_mutex around the whole on/off sequence. + * Can be nested with intel_edp_panel_vdd_{on,off}() calls. + */ +static bool edp_panel_vdd_on(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); @@ -1190,6 +1362,8 @@ static bool _edp_panel_vdd_on(struct intel_dp *intel_dp) u32 pp_stat_reg, pp_ctrl_reg; bool need_to_disable = !intel_dp->want_panel_vdd; + lockdep_assert_held(&dev_priv->pps_mutex); + if (!is_edp(intel_dp)) return false; @@ -1227,62 +1401,76 @@ static bool _edp_panel_vdd_on(struct intel_dp *intel_dp) return need_to_disable; } +/* + * Must be paired with intel_edp_panel_vdd_off() or + * intel_edp_panel_off(). + * Nested calls to these functions are not allowed since + * we drop the lock. Caller must use some higher level + * locking to prevent nested calls from other threads. + */ void intel_edp_panel_vdd_on(struct intel_dp *intel_dp) { - if (is_edp(intel_dp)) { - bool vdd = _edp_panel_vdd_on(intel_dp); + bool vdd; - WARN(!vdd, "eDP VDD already requested on\n"); - } + if (!is_edp(intel_dp)) + return; + + pps_lock(intel_dp); + vdd = edp_panel_vdd_on(intel_dp); + pps_unlock(intel_dp); + + WARN(!vdd, "eDP VDD already requested on\n"); } static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_digital_port *intel_dig_port = + dp_to_dig_port(intel_dp); + struct intel_encoder *intel_encoder = &intel_dig_port->base; + enum intel_display_power_domain power_domain; u32 pp; u32 pp_stat_reg, pp_ctrl_reg; - WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); + lockdep_assert_held(&dev_priv->pps_mutex); - if (!intel_dp->want_panel_vdd && edp_have_panel_vdd(intel_dp)) { - struct intel_digital_port *intel_dig_port = - dp_to_dig_port(intel_dp); - struct intel_encoder *intel_encoder = &intel_dig_port->base; - enum intel_display_power_domain power_domain; + WARN_ON(intel_dp->want_panel_vdd); - DRM_DEBUG_KMS("Turning eDP VDD off\n"); + if (!edp_have_panel_vdd(intel_dp)) + return; - pp = ironlake_get_pp_control(intel_dp); - pp &= ~EDP_FORCE_VDD; + DRM_DEBUG_KMS("Turning eDP VDD off\n"); - pp_ctrl_reg = _pp_ctrl_reg(intel_dp); - pp_stat_reg = _pp_stat_reg(intel_dp); + pp = ironlake_get_pp_control(intel_dp); + pp &= ~EDP_FORCE_VDD; - I915_WRITE(pp_ctrl_reg, pp); - POSTING_READ(pp_ctrl_reg); + pp_ctrl_reg = _pp_ctrl_reg(intel_dp); + pp_stat_reg = _pp_stat_reg(intel_dp); - /* Make sure sequencer is idle before allowing subsequent activity */ - DRM_DEBUG_KMS("PP_STATUS: 0x%08x PP_CONTROL: 0x%08x\n", - I915_READ(pp_stat_reg), I915_READ(pp_ctrl_reg)); + I915_WRITE(pp_ctrl_reg, pp); + POSTING_READ(pp_ctrl_reg); - if ((pp & POWER_TARGET_ON) == 0) - intel_dp->last_power_cycle = jiffies; + /* Make sure sequencer is idle before allowing subsequent activity */ + DRM_DEBUG_KMS("PP_STATUS: 0x%08x PP_CONTROL: 0x%08x\n", + I915_READ(pp_stat_reg), I915_READ(pp_ctrl_reg)); - power_domain = intel_display_port_power_domain(intel_encoder); - intel_display_power_put(dev_priv, power_domain); - } + if ((pp & POWER_TARGET_ON) == 0) + intel_dp->last_power_cycle = jiffies; + + power_domain = intel_display_port_power_domain(intel_encoder); + intel_display_power_put(dev_priv, power_domain); } static void edp_panel_vdd_work(struct work_struct *__work) { struct intel_dp *intel_dp = container_of(to_delayed_work(__work), struct intel_dp, panel_vdd_work); - struct drm_device *dev = intel_dp_to_dev(intel_dp); - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - edp_panel_vdd_off_sync(intel_dp); - drm_modeset_unlock(&dev->mode_config.connection_mutex); + pps_lock(intel_dp); + if (!intel_dp->want_panel_vdd) + edp_panel_vdd_off_sync(intel_dp); + pps_unlock(intel_dp); } static void edp_panel_vdd_schedule_off(struct intel_dp *intel_dp) @@ -1298,8 +1486,18 @@ static void edp_panel_vdd_schedule_off(struct intel_dp *intel_dp) schedule_delayed_work(&intel_dp->panel_vdd_work, delay); } +/* + * Must be paired with edp_panel_vdd_on(). + * Must hold pps_mutex around the whole on/off sequence. + * Can be nested with intel_edp_panel_vdd_{on,off}() calls. + */ static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync) { + struct drm_i915_private *dev_priv = + intel_dp_to_dev(intel_dp)->dev_private; + + lockdep_assert_held(&dev_priv->pps_mutex); + if (!is_edp(intel_dp)) return; @@ -1313,6 +1511,22 @@ static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync) edp_panel_vdd_schedule_off(intel_dp); } +/* + * Must be paired with intel_edp_panel_vdd_on(). + * Nested calls to these functions are not allowed since + * we drop the lock. Caller must use some higher level + * locking to prevent nested calls from other threads. + */ +static void intel_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync) +{ + if (!is_edp(intel_dp)) + return; + + pps_lock(intel_dp); + edp_panel_vdd_off(intel_dp, sync); + pps_unlock(intel_dp); +} + void intel_edp_panel_on(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); @@ -1325,9 +1539,11 @@ void intel_edp_panel_on(struct intel_dp *intel_dp) DRM_DEBUG_KMS("Turn eDP power on\n"); + pps_lock(intel_dp); + if (edp_have_panel_power(intel_dp)) { DRM_DEBUG_KMS("eDP power already on\n"); - return; + goto out; } wait_panel_power_cycle(intel_dp); @@ -1356,6 +1572,9 @@ void intel_edp_panel_on(struct intel_dp *intel_dp) I915_WRITE(pp_ctrl_reg, pp); POSTING_READ(pp_ctrl_reg); } + + out: + pps_unlock(intel_dp); } void intel_edp_panel_off(struct intel_dp *intel_dp) @@ -1373,6 +1592,8 @@ void intel_edp_panel_off(struct intel_dp *intel_dp) DRM_DEBUG_KMS("Turn eDP power off\n"); + pps_lock(intel_dp); + WARN(!intel_dp->want_panel_vdd, "Need VDD to turn off panel\n"); pp = ironlake_get_pp_control(intel_dp); @@ -1394,9 +1615,12 @@ void intel_edp_panel_off(struct intel_dp *intel_dp) /* We got a reference when we enabled the VDD. */ power_domain = intel_display_port_power_domain(intel_encoder); intel_display_power_put(dev_priv, power_domain); + + pps_unlock(intel_dp); } -void intel_edp_backlight_on(struct intel_dp *intel_dp) +/* Enable backlight in the panel power control. */ +static void _intel_edp_backlight_on(struct intel_dp *intel_dp) { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct drm_device *dev = intel_dig_port->base.base.dev; @@ -1404,13 +1628,6 @@ void intel_edp_backlight_on(struct intel_dp *intel_dp) u32 pp; u32 pp_ctrl_reg; - if (!is_edp(intel_dp)) - return; - - DRM_DEBUG_KMS("\n"); - - intel_panel_enable_backlight(intel_dp->attached_connector); - /* * If we enable the backlight right away following a panel power * on, we may see slight flicker as the panel syncs with the eDP @@ -1418,6 +1635,9 @@ void intel_edp_backlight_on(struct intel_dp *intel_dp) * allowing it to appear. */ wait_backlight_on(intel_dp); + + pps_lock(intel_dp); + pp = ironlake_get_pp_control(intel_dp); pp |= EDP_BLC_ENABLE; @@ -1425,9 +1645,24 @@ void intel_edp_backlight_on(struct intel_dp *intel_dp) I915_WRITE(pp_ctrl_reg, pp); POSTING_READ(pp_ctrl_reg); + + pps_unlock(intel_dp); } -void intel_edp_backlight_off(struct intel_dp *intel_dp) +/* Enable backlight PWM and backlight PP control. */ +void intel_edp_backlight_on(struct intel_dp *intel_dp) +{ + if (!is_edp(intel_dp)) + return; + + DRM_DEBUG_KMS("\n"); + + intel_panel_enable_backlight(intel_dp->attached_connector); + _intel_edp_backlight_on(intel_dp); +} + +/* Disable backlight in the panel power control. */ +static void _intel_edp_backlight_off(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; @@ -1437,7 +1672,8 @@ void intel_edp_backlight_off(struct intel_dp *intel_dp) if (!is_edp(intel_dp)) return; - DRM_DEBUG_KMS("\n"); + pps_lock(intel_dp); + pp = ironlake_get_pp_control(intel_dp); pp &= ~EDP_BLC_ENABLE; @@ -1445,13 +1681,51 @@ void intel_edp_backlight_off(struct intel_dp *intel_dp) I915_WRITE(pp_ctrl_reg, pp); POSTING_READ(pp_ctrl_reg); - intel_dp->last_backlight_off = jiffies; + pps_unlock(intel_dp); + + intel_dp->last_backlight_off = jiffies; edp_wait_backlight_off(intel_dp); +} +/* Disable backlight PP control and backlight PWM. */ +void intel_edp_backlight_off(struct intel_dp *intel_dp) +{ + if (!is_edp(intel_dp)) + return; + + DRM_DEBUG_KMS("\n"); + + _intel_edp_backlight_off(intel_dp); intel_panel_disable_backlight(intel_dp->attached_connector); } +/* + * Hook for controlling the panel power control backlight through the bl_power + * sysfs attribute. Take care to handle multiple calls. + */ +static void intel_edp_backlight_power(struct intel_connector *connector, + bool enable) +{ + struct intel_dp *intel_dp = intel_attached_dp(&connector->base); + bool is_enabled; + + pps_lock(intel_dp); + is_enabled = ironlake_get_pp_control(intel_dp) & EDP_BLC_ENABLE; + pps_unlock(intel_dp); + + if (is_enabled == enable) + return; + + DRM_DEBUG_KMS("panel power control backlight %s\n", + enable ? "enable" : "disable"); + + if (enable) + _intel_edp_backlight_on(intel_dp); + else + _intel_edp_backlight_off(intel_dp); +} + static void ironlake_edp_pll_on(struct intel_dp *intel_dp) { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); @@ -1515,8 +1789,6 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) if (mode != DRM_MODE_DPMS_ON) { ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER, DP_SET_POWER_D3); - if (ret != 1) - DRM_DEBUG_DRIVER("failed to write sink power state\n"); } else { /* * When turning on, we need to retry for 1ms to give the sink @@ -1530,6 +1802,10 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) msleep(1); } } + + if (ret != 1) + DRM_DEBUG_KMS("failed to %s sink power state\n", + mode == DRM_MODE_DPMS_ON ? "enable" : "disable"); } static bool intel_dp_get_hw_state(struct intel_encoder *encoder, @@ -1576,7 +1852,7 @@ static bool intel_dp_get_hw_state(struct intel_encoder *encoder, return true; } - for_each_pipe(i) { + for_each_pipe(dev_priv, i) { trans_dp = I915_READ(TRANS_DP_CTL(i)); if ((trans_dp & TRANS_DP_PORT_SEL_MASK) == trans_sel) { *pipe = i; @@ -2036,7 +2312,6 @@ void intel_edp_psr_init(struct drm_device *dev) static void intel_disable_dp(struct intel_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); - enum port port = dp_to_dig_port(intel_dp)->port; struct drm_device *dev = encoder->base.dev; /* Make sure the panel is off before trying to change the mode. But also @@ -2046,21 +2321,19 @@ static void intel_disable_dp(struct intel_encoder *encoder) intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF); intel_edp_panel_off(intel_dp); - /* cpu edp my only be disable _after_ the cpu pipe/plane is disabled. */ - if (!(port == PORT_A || IS_VALLEYVIEW(dev))) + /* disable the port before the pipe on g4x */ + if (INTEL_INFO(dev)->gen < 5) intel_dp_link_down(intel_dp); } -static void g4x_post_disable_dp(struct intel_encoder *encoder) +static void ilk_post_disable_dp(struct intel_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); enum port port = dp_to_dig_port(intel_dp)->port; - if (port != PORT_A) - return; - intel_dp_link_down(intel_dp); - ironlake_edp_pll_off(intel_dp); + if (port == PORT_A) + ironlake_edp_pll_off(intel_dp); } static void vlv_post_disable_dp(struct intel_encoder *encoder) @@ -2103,7 +2376,105 @@ static void chv_post_disable_dp(struct intel_encoder *encoder) val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); - mutex_unlock(&dev_priv->dpio_lock); + mutex_unlock(&dev_priv->dpio_lock); +} + +static void +_intel_dp_set_link_train(struct intel_dp *intel_dp, + uint32_t *DP, + uint8_t dp_train_pat) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum port port = intel_dig_port->port; + + if (HAS_DDI(dev)) { + uint32_t temp = I915_READ(DP_TP_CTL(port)); + + if (dp_train_pat & DP_LINK_SCRAMBLING_DISABLE) + temp |= DP_TP_CTL_SCRAMBLE_DISABLE; + else + temp &= ~DP_TP_CTL_SCRAMBLE_DISABLE; + + temp &= ~DP_TP_CTL_LINK_TRAIN_MASK; + switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) { + case DP_TRAINING_PATTERN_DISABLE: + temp |= DP_TP_CTL_LINK_TRAIN_NORMAL; + + break; + case DP_TRAINING_PATTERN_1: + temp |= DP_TP_CTL_LINK_TRAIN_PAT1; + break; + case DP_TRAINING_PATTERN_2: + temp |= DP_TP_CTL_LINK_TRAIN_PAT2; + break; + case DP_TRAINING_PATTERN_3: + temp |= DP_TP_CTL_LINK_TRAIN_PAT3; + break; + } + I915_WRITE(DP_TP_CTL(port), temp); + + } else if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || port != PORT_A)) { + *DP &= ~DP_LINK_TRAIN_MASK_CPT; + + switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) { + case DP_TRAINING_PATTERN_DISABLE: + *DP |= DP_LINK_TRAIN_OFF_CPT; + break; + case DP_TRAINING_PATTERN_1: + *DP |= DP_LINK_TRAIN_PAT_1_CPT; + break; + case DP_TRAINING_PATTERN_2: + *DP |= DP_LINK_TRAIN_PAT_2_CPT; + break; + case DP_TRAINING_PATTERN_3: + DRM_ERROR("DP training pattern 3 not supported\n"); + *DP |= DP_LINK_TRAIN_PAT_2_CPT; + break; + } + + } else { + if (IS_CHERRYVIEW(dev)) + *DP &= ~DP_LINK_TRAIN_MASK_CHV; + else + *DP &= ~DP_LINK_TRAIN_MASK; + + switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) { + case DP_TRAINING_PATTERN_DISABLE: + *DP |= DP_LINK_TRAIN_OFF; + break; + case DP_TRAINING_PATTERN_1: + *DP |= DP_LINK_TRAIN_PAT_1; + break; + case DP_TRAINING_PATTERN_2: + *DP |= DP_LINK_TRAIN_PAT_2; + break; + case DP_TRAINING_PATTERN_3: + if (IS_CHERRYVIEW(dev)) { + *DP |= DP_LINK_TRAIN_PAT_3_CHV; + } else { + DRM_ERROR("DP training pattern 3 not supported\n"); + *DP |= DP_LINK_TRAIN_PAT_2; + } + break; + } + } +} + +static void intel_dp_enable_port(struct intel_dp *intel_dp) +{ + struct drm_device *dev = intel_dp_to_dev(intel_dp); + struct drm_i915_private *dev_priv = dev->dev_private; + + intel_dp->DP |= DP_PORT_EN; + + /* enable with pattern 1 (as per spec) */ + _intel_dp_set_link_train(intel_dp, &intel_dp->DP, + DP_TRAINING_PATTERN_1); + + I915_WRITE(intel_dp->output_reg, intel_dp->DP); + POSTING_READ(intel_dp->output_reg); } static void intel_enable_dp(struct intel_encoder *encoder) @@ -2116,11 +2487,12 @@ static void intel_enable_dp(struct intel_encoder *encoder) if (WARN_ON(dp_reg & DP_PORT_EN)) return; + intel_dp_enable_port(intel_dp); intel_edp_panel_vdd_on(intel_dp); + intel_edp_panel_on(intel_dp); + intel_edp_panel_vdd_off(intel_dp, true); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); intel_dp_start_link_train(intel_dp); - intel_edp_panel_on(intel_dp); - edp_panel_vdd_off(intel_dp, true); intel_dp_complete_link_train(intel_dp); intel_dp_stop_link_train(intel_dp); } @@ -2154,6 +2526,78 @@ static void g4x_pre_enable_dp(struct intel_encoder *encoder) } } +static void vlv_steal_power_sequencer(struct drm_device *dev, + enum pipe pipe) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_encoder *encoder; + + lockdep_assert_held(&dev_priv->pps_mutex); + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + struct intel_dp *intel_dp; + enum port port; + + if (encoder->type != INTEL_OUTPUT_EDP) + continue; + + intel_dp = enc_to_intel_dp(&encoder->base); + port = dp_to_dig_port(intel_dp)->port; + + if (intel_dp->pps_pipe != pipe) + continue; + + DRM_DEBUG_KMS("stealing pipe %c power sequencer from port %c\n", + pipe_name(pipe), port_name(port)); + + /* make sure vdd is off before we steal it */ + edp_panel_vdd_off_sync(intel_dp); + + intel_dp->pps_pipe = INVALID_PIPE; + } +} + +static void vlv_init_panel_power_sequencer(struct intel_dp *intel_dp) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct intel_encoder *encoder = &intel_dig_port->base; + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); + struct edp_power_seq power_seq; + + lockdep_assert_held(&dev_priv->pps_mutex); + + if (intel_dp->pps_pipe == crtc->pipe) + return; + + /* + * If another power sequencer was being used on this + * port previously make sure to turn off vdd there while + * we still have control of it. + */ + if (intel_dp->pps_pipe != INVALID_PIPE) + edp_panel_vdd_off_sync(intel_dp); + + /* + * We may be stealing the power + * sequencer from another port. + */ + vlv_steal_power_sequencer(dev, crtc->pipe); + + /* now it's all ours */ + intel_dp->pps_pipe = crtc->pipe; + + DRM_DEBUG_KMS("initializing pipe %c power sequencer for port %c\n", + pipe_name(intel_dp->pps_pipe), port_name(intel_dig_port->port)); + + /* init power sequencer on this pipe and port */ + intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); + intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, + &power_seq); +} + static void vlv_pre_enable_dp(struct intel_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); @@ -2163,7 +2607,6 @@ static void vlv_pre_enable_dp(struct intel_encoder *encoder) struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); enum dpio_channel port = vlv_dport_to_channel(dport); int pipe = intel_crtc->pipe; - struct edp_power_seq power_seq; u32 val; mutex_lock(&dev_priv->dpio_lock); @@ -2182,10 +2625,9 @@ static void vlv_pre_enable_dp(struct intel_encoder *encoder) mutex_unlock(&dev_priv->dpio_lock); if (is_edp(intel_dp)) { - /* init power sequencer on this pipe and port */ - intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); - intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, - &power_seq); + pps_lock(intel_dp); + vlv_init_panel_power_sequencer(intel_dp); + pps_unlock(intel_dp); } intel_enable_dp(encoder); @@ -2229,7 +2671,6 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) struct intel_digital_port *dport = dp_to_dig_port(intel_dp); struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct edp_power_seq power_seq; struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); enum dpio_channel ch = vlv_dport_to_channel(dport); @@ -2275,10 +2716,9 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder) mutex_unlock(&dev_priv->dpio_lock); if (is_edp(intel_dp)) { - /* init power sequencer on this pipe and port */ - intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); - intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, - &power_seq); + pps_lock(intel_dp); + vlv_init_panel_power_sequencer(intel_dp); + pps_unlock(intel_dp); } intel_enable_dp(encoder); @@ -2297,6 +2737,8 @@ static void chv_dp_pre_pll_enable(struct intel_encoder *encoder) enum pipe pipe = intel_crtc->pipe; u32 val; + intel_dp_prepare(encoder); + mutex_lock(&dev_priv->dpio_lock); /* program left/right clock distribution */ @@ -2395,13 +2837,13 @@ intel_dp_voltage_max(struct intel_dp *intel_dp) enum port port = dp_to_dig_port(intel_dp)->port; if (IS_VALLEYVIEW(dev)) - return DP_TRAIN_VOLTAGE_SWING_1200; + return DP_TRAIN_VOLTAGE_SWING_LEVEL_3; else if (IS_GEN7(dev) && port == PORT_A) - return DP_TRAIN_VOLTAGE_SWING_800; + return DP_TRAIN_VOLTAGE_SWING_LEVEL_2; else if (HAS_PCH_CPT(dev) && port != PORT_A) - return DP_TRAIN_VOLTAGE_SWING_1200; + return DP_TRAIN_VOLTAGE_SWING_LEVEL_3; else - return DP_TRAIN_VOLTAGE_SWING_800; + return DP_TRAIN_VOLTAGE_SWING_LEVEL_2; } static uint8_t @@ -2412,49 +2854,49 @@ intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing) if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: - return DP_TRAIN_PRE_EMPHASIS_9_5; - case DP_TRAIN_VOLTAGE_SWING_600: - return DP_TRAIN_PRE_EMPHASIS_6; - case DP_TRAIN_VOLTAGE_SWING_800: - return DP_TRAIN_PRE_EMPHASIS_3_5; - case DP_TRAIN_VOLTAGE_SWING_1200: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: + return DP_TRAIN_PRE_EMPH_LEVEL_3; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: + return DP_TRAIN_PRE_EMPH_LEVEL_2; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: + return DP_TRAIN_PRE_EMPH_LEVEL_1; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_3: default: - return DP_TRAIN_PRE_EMPHASIS_0; + return DP_TRAIN_PRE_EMPH_LEVEL_0; } } else if (IS_VALLEYVIEW(dev)) { switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: - return DP_TRAIN_PRE_EMPHASIS_9_5; - case DP_TRAIN_VOLTAGE_SWING_600: - return DP_TRAIN_PRE_EMPHASIS_6; - case DP_TRAIN_VOLTAGE_SWING_800: - return DP_TRAIN_PRE_EMPHASIS_3_5; - case DP_TRAIN_VOLTAGE_SWING_1200: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: + return DP_TRAIN_PRE_EMPH_LEVEL_3; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: + return DP_TRAIN_PRE_EMPH_LEVEL_2; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: + return DP_TRAIN_PRE_EMPH_LEVEL_1; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_3: default: - return DP_TRAIN_PRE_EMPHASIS_0; + return DP_TRAIN_PRE_EMPH_LEVEL_0; } } else if (IS_GEN7(dev) && port == PORT_A) { switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: - return DP_TRAIN_PRE_EMPHASIS_6; - case DP_TRAIN_VOLTAGE_SWING_600: - case DP_TRAIN_VOLTAGE_SWING_800: - return DP_TRAIN_PRE_EMPHASIS_3_5; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: + return DP_TRAIN_PRE_EMPH_LEVEL_2; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: + return DP_TRAIN_PRE_EMPH_LEVEL_1; default: - return DP_TRAIN_PRE_EMPHASIS_0; + return DP_TRAIN_PRE_EMPH_LEVEL_0; } } else { switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: - return DP_TRAIN_PRE_EMPHASIS_6; - case DP_TRAIN_VOLTAGE_SWING_600: - return DP_TRAIN_PRE_EMPHASIS_6; - case DP_TRAIN_VOLTAGE_SWING_800: - return DP_TRAIN_PRE_EMPHASIS_3_5; - case DP_TRAIN_VOLTAGE_SWING_1200: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: + return DP_TRAIN_PRE_EMPH_LEVEL_2; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: + return DP_TRAIN_PRE_EMPH_LEVEL_2; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: + return DP_TRAIN_PRE_EMPH_LEVEL_1; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_3: default: - return DP_TRAIN_PRE_EMPHASIS_0; + return DP_TRAIN_PRE_EMPH_LEVEL_0; } } } @@ -2473,22 +2915,22 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp) int pipe = intel_crtc->pipe; switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) { - case DP_TRAIN_PRE_EMPHASIS_0: + case DP_TRAIN_PRE_EMPH_LEVEL_0: preemph_reg_value = 0x0004000; switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: demph_reg_value = 0x2B405555; uniqtranscale_reg_value = 0x552AB83A; break; - case DP_TRAIN_VOLTAGE_SWING_600: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: demph_reg_value = 0x2B404040; uniqtranscale_reg_value = 0x5548B83A; break; - case DP_TRAIN_VOLTAGE_SWING_800: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: demph_reg_value = 0x2B245555; uniqtranscale_reg_value = 0x5560B83A; break; - case DP_TRAIN_VOLTAGE_SWING_1200: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_3: demph_reg_value = 0x2B405555; uniqtranscale_reg_value = 0x5598DA3A; break; @@ -2496,18 +2938,18 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp) return 0; } break; - case DP_TRAIN_PRE_EMPHASIS_3_5: + case DP_TRAIN_PRE_EMPH_LEVEL_1: preemph_reg_value = 0x0002000; switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: demph_reg_value = 0x2B404040; uniqtranscale_reg_value = 0x5552B83A; break; - case DP_TRAIN_VOLTAGE_SWING_600: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: demph_reg_value = 0x2B404848; uniqtranscale_reg_value = 0x5580B83A; break; - case DP_TRAIN_VOLTAGE_SWING_800: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: demph_reg_value = 0x2B404040; uniqtranscale_reg_value = 0x55ADDA3A; break; @@ -2515,14 +2957,14 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp) return 0; } break; - case DP_TRAIN_PRE_EMPHASIS_6: + case DP_TRAIN_PRE_EMPH_LEVEL_2: preemph_reg_value = 0x0000000; switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: demph_reg_value = 0x2B305555; uniqtranscale_reg_value = 0x5570B83A; break; - case DP_TRAIN_VOLTAGE_SWING_600: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: demph_reg_value = 0x2B2B4040; uniqtranscale_reg_value = 0x55ADDA3A; break; @@ -2530,10 +2972,10 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp) return 0; } break; - case DP_TRAIN_PRE_EMPHASIS_9_5: + case DP_TRAIN_PRE_EMPH_LEVEL_3: preemph_reg_value = 0x0006000; switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: demph_reg_value = 0x1B405555; uniqtranscale_reg_value = 0x55ADDA3A; break; @@ -2572,21 +3014,21 @@ static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp) int i; switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) { - case DP_TRAIN_PRE_EMPHASIS_0: + case DP_TRAIN_PRE_EMPH_LEVEL_0: switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: deemph_reg_value = 128; margin_reg_value = 52; break; - case DP_TRAIN_VOLTAGE_SWING_600: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: deemph_reg_value = 128; margin_reg_value = 77; break; - case DP_TRAIN_VOLTAGE_SWING_800: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: deemph_reg_value = 128; margin_reg_value = 102; break; - case DP_TRAIN_VOLTAGE_SWING_1200: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_3: deemph_reg_value = 128; margin_reg_value = 154; /* FIXME extra to set for 1200 */ @@ -2595,17 +3037,17 @@ static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp) return 0; } break; - case DP_TRAIN_PRE_EMPHASIS_3_5: + case DP_TRAIN_PRE_EMPH_LEVEL_1: switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: deemph_reg_value = 85; margin_reg_value = 78; break; - case DP_TRAIN_VOLTAGE_SWING_600: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: deemph_reg_value = 85; margin_reg_value = 116; break; - case DP_TRAIN_VOLTAGE_SWING_800: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: deemph_reg_value = 85; margin_reg_value = 154; break; @@ -2613,13 +3055,13 @@ static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp) return 0; } break; - case DP_TRAIN_PRE_EMPHASIS_6: + case DP_TRAIN_PRE_EMPH_LEVEL_2: switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: deemph_reg_value = 64; margin_reg_value = 104; break; - case DP_TRAIN_VOLTAGE_SWING_600: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: deemph_reg_value = 64; margin_reg_value = 154; break; @@ -2627,9 +3069,9 @@ static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp) return 0; } break; - case DP_TRAIN_PRE_EMPHASIS_9_5: + case DP_TRAIN_PRE_EMPH_LEVEL_3: switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: deemph_reg_value = 43; margin_reg_value = 154; break; @@ -2663,8 +3105,8 @@ static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp) /* Program swing margin */ for (i = 0; i < 4; i++) { val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i)); - val &= ~DPIO_SWING_MARGIN_MASK; - val |= margin_reg_value << DPIO_SWING_MARGIN_SHIFT; + val &= ~DPIO_SWING_MARGIN000_MASK; + val |= margin_reg_value << DPIO_SWING_MARGIN000_SHIFT; vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val); } @@ -2676,9 +3118,9 @@ static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp) } if (((train_set & DP_TRAIN_PRE_EMPHASIS_MASK) - == DP_TRAIN_PRE_EMPHASIS_0) && + == DP_TRAIN_PRE_EMPH_LEVEL_0) && ((train_set & DP_TRAIN_VOLTAGE_SWING_MASK) - == DP_TRAIN_VOLTAGE_SWING_1200)) { + == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)) { /* * The document said it needs to set bit 27 for ch0 and bit 26 @@ -2757,32 +3199,32 @@ intel_gen4_signal_levels(uint8_t train_set) uint32_t signal_levels = 0; switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { - case DP_TRAIN_VOLTAGE_SWING_400: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0: default: signal_levels |= DP_VOLTAGE_0_4; break; - case DP_TRAIN_VOLTAGE_SWING_600: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1: signal_levels |= DP_VOLTAGE_0_6; break; - case DP_TRAIN_VOLTAGE_SWING_800: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2: signal_levels |= DP_VOLTAGE_0_8; break; - case DP_TRAIN_VOLTAGE_SWING_1200: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_3: signal_levels |= DP_VOLTAGE_1_2; break; } switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) { - case DP_TRAIN_PRE_EMPHASIS_0: + case DP_TRAIN_PRE_EMPH_LEVEL_0: default: signal_levels |= DP_PRE_EMPHASIS_0; break; - case DP_TRAIN_PRE_EMPHASIS_3_5: + case DP_TRAIN_PRE_EMPH_LEVEL_1: signal_levels |= DP_PRE_EMPHASIS_3_5; break; - case DP_TRAIN_PRE_EMPHASIS_6: + case DP_TRAIN_PRE_EMPH_LEVEL_2: signal_levels |= DP_PRE_EMPHASIS_6; break; - case DP_TRAIN_PRE_EMPHASIS_9_5: + case DP_TRAIN_PRE_EMPH_LEVEL_3: signal_levels |= DP_PRE_EMPHASIS_9_5; break; } @@ -2796,19 +3238,19 @@ intel_gen6_edp_signal_levels(uint8_t train_set) int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK | DP_TRAIN_PRE_EMPHASIS_MASK); switch (signal_levels) { - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0: - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_0: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_0: return EDP_LINK_TRAIN_400_600MV_0DB_SNB_B; - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_1: return EDP_LINK_TRAIN_400MV_3_5DB_SNB_B; - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6: - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_6: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_2: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_2: return EDP_LINK_TRAIN_400_600MV_6DB_SNB_B; - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5: - case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_1: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_1: return EDP_LINK_TRAIN_600_800MV_3_5DB_SNB_B; - case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0: - case DP_TRAIN_VOLTAGE_SWING_1200 | DP_TRAIN_PRE_EMPHASIS_0: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_0: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_3 | DP_TRAIN_PRE_EMPH_LEVEL_0: return EDP_LINK_TRAIN_800_1200MV_0DB_SNB_B; default: DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level:" @@ -2824,21 +3266,21 @@ intel_gen7_edp_signal_levels(uint8_t train_set) int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK | DP_TRAIN_PRE_EMPHASIS_MASK); switch (signal_levels) { - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_0: return EDP_LINK_TRAIN_400MV_0DB_IVB; - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_1: return EDP_LINK_TRAIN_400MV_3_5DB_IVB; - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_2: return EDP_LINK_TRAIN_400MV_6DB_IVB; - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_0: return EDP_LINK_TRAIN_600MV_0DB_IVB; - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_1: return EDP_LINK_TRAIN_600MV_3_5DB_IVB; - case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_0: return EDP_LINK_TRAIN_800MV_0DB_IVB; - case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5: + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_1: return EDP_LINK_TRAIN_800MV_3_5DB_IVB; default: @@ -2855,30 +3297,30 @@ intel_hsw_signal_levels(uint8_t train_set) int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK | DP_TRAIN_PRE_EMPHASIS_MASK); switch (signal_levels) { - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0: - return DDI_BUF_EMP_400MV_0DB_HSW; - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5: - return DDI_BUF_EMP_400MV_3_5DB_HSW; - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6: - return DDI_BUF_EMP_400MV_6DB_HSW; - case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_9_5: - return DDI_BUF_EMP_400MV_9_5DB_HSW; - - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0: - return DDI_BUF_EMP_600MV_0DB_HSW; - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5: - return DDI_BUF_EMP_600MV_3_5DB_HSW; - case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_6: - return DDI_BUF_EMP_600MV_6DB_HSW; - - case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0: - return DDI_BUF_EMP_800MV_0DB_HSW; - case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5: - return DDI_BUF_EMP_800MV_3_5DB_HSW; + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_0: + return DDI_BUF_TRANS_SELECT(0); + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_1: + return DDI_BUF_TRANS_SELECT(1); + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_2: + return DDI_BUF_TRANS_SELECT(2); + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_3: + return DDI_BUF_TRANS_SELECT(3); + + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_0: + return DDI_BUF_TRANS_SELECT(4); + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_1: + return DDI_BUF_TRANS_SELECT(5); + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_2: + return DDI_BUF_TRANS_SELECT(6); + + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_0: + return DDI_BUF_TRANS_SELECT(7); + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_1: + return DDI_BUF_TRANS_SELECT(8); default: DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level:" "0x%x\n", signal_levels); - return DDI_BUF_EMP_400MV_0DB_HSW; + return DDI_BUF_TRANS_SELECT(0); } } @@ -2925,74 +3367,10 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct drm_device *dev = intel_dig_port->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - enum port port = intel_dig_port->port; uint8_t buf[sizeof(intel_dp->train_set) + 1]; int ret, len; - if (HAS_DDI(dev)) { - uint32_t temp = I915_READ(DP_TP_CTL(port)); - - if (dp_train_pat & DP_LINK_SCRAMBLING_DISABLE) - temp |= DP_TP_CTL_SCRAMBLE_DISABLE; - else - temp &= ~DP_TP_CTL_SCRAMBLE_DISABLE; - - temp &= ~DP_TP_CTL_LINK_TRAIN_MASK; - switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) { - case DP_TRAINING_PATTERN_DISABLE: - temp |= DP_TP_CTL_LINK_TRAIN_NORMAL; - - break; - case DP_TRAINING_PATTERN_1: - temp |= DP_TP_CTL_LINK_TRAIN_PAT1; - break; - case DP_TRAINING_PATTERN_2: - temp |= DP_TP_CTL_LINK_TRAIN_PAT2; - break; - case DP_TRAINING_PATTERN_3: - temp |= DP_TP_CTL_LINK_TRAIN_PAT3; - break; - } - I915_WRITE(DP_TP_CTL(port), temp); - - } else if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || port != PORT_A)) { - *DP &= ~DP_LINK_TRAIN_MASK_CPT; - - switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) { - case DP_TRAINING_PATTERN_DISABLE: - *DP |= DP_LINK_TRAIN_OFF_CPT; - break; - case DP_TRAINING_PATTERN_1: - *DP |= DP_LINK_TRAIN_PAT_1_CPT; - break; - case DP_TRAINING_PATTERN_2: - *DP |= DP_LINK_TRAIN_PAT_2_CPT; - break; - case DP_TRAINING_PATTERN_3: - DRM_ERROR("DP training pattern 3 not supported\n"); - *DP |= DP_LINK_TRAIN_PAT_2_CPT; - break; - } - - } else { - *DP &= ~DP_LINK_TRAIN_MASK; - - switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) { - case DP_TRAINING_PATTERN_DISABLE: - *DP |= DP_LINK_TRAIN_OFF; - break; - case DP_TRAINING_PATTERN_1: - *DP |= DP_LINK_TRAIN_PAT_1; - break; - case DP_TRAINING_PATTERN_2: - *DP |= DP_LINK_TRAIN_PAT_2; - break; - case DP_TRAINING_PATTERN_3: - DRM_ERROR("DP training pattern 3 not supported\n"); - *DP |= DP_LINK_TRAIN_PAT_2; - break; - } - } + _intel_dp_set_link_train(intel_dp, DP, dp_train_pat); I915_WRITE(intel_dp->output_reg, *DP); POSTING_READ(intel_dp->output_reg); @@ -3276,7 +3654,10 @@ intel_dp_link_down(struct intel_dp *intel_dp) DP &= ~DP_LINK_TRAIN_MASK_CPT; I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE_CPT); } else { - DP &= ~DP_LINK_TRAIN_MASK; + if (IS_CHERRYVIEW(dev)) + DP &= ~DP_LINK_TRAIN_MASK_CHV; + else + DP &= ~DP_LINK_TRAIN_MASK; I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE); } POSTING_READ(intel_dp->output_reg); @@ -3322,15 +3703,11 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) struct drm_device *dev = dig_port->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - char dpcd_hex_dump[sizeof(intel_dp->dpcd) * 3]; - if (intel_dp_dpcd_read_wake(&intel_dp->aux, 0x000, intel_dp->dpcd, sizeof(intel_dp->dpcd)) < 0) return false; /* aux transfer failed */ - hex_dump_to_buffer(intel_dp->dpcd, sizeof(intel_dp->dpcd), - 32, 1, dpcd_hex_dump, sizeof(dpcd_hex_dump), false); - DRM_DEBUG_KMS("DPCD: %s\n", dpcd_hex_dump); + DRM_DEBUG_KMS("DPCD: %*ph\n", (int) sizeof(intel_dp->dpcd), intel_dp->dpcd); if (intel_dp->dpcd[DP_DPCD_REV] == 0) return false; /* DPCD not present */ @@ -3351,7 +3728,7 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) if (intel_dp->dpcd[DP_DPCD_REV] >= 0x12 && intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_TPS3_SUPPORTED) { intel_dp->use_tps3 = true; - DRM_DEBUG_KMS("Displayport TPS3 supported"); + DRM_DEBUG_KMS("Displayport TPS3 supported\n"); } else intel_dp->use_tps3 = false; @@ -3388,7 +3765,7 @@ intel_dp_probe_oui(struct intel_dp *intel_dp) DRM_DEBUG_KMS("Branch OUI: %02hx%02hx%02hx\n", buf[0], buf[1], buf[2]); - edp_panel_vdd_off(intel_dp, false); + intel_edp_panel_vdd_off(intel_dp, false); } static bool @@ -3402,7 +3779,7 @@ intel_dp_probe_mst(struct intel_dp *intel_dp) if (intel_dp->dpcd[DP_DPCD_REV] < 0x12) return false; - _edp_panel_vdd_on(intel_dp); + intel_edp_panel_vdd_on(intel_dp); if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_MSTM_CAP, buf, 1)) { if (buf[0] & DP_MST_CAP) { DRM_DEBUG_KMS("Sink is MST capable\n"); @@ -3412,7 +3789,7 @@ intel_dp_probe_mst(struct intel_dp *intel_dp) intel_dp->is_mst = false; } } - edp_panel_vdd_off(intel_dp, false); + intel_edp_panel_vdd_off(intel_dp, false); drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst); return intel_dp->is_mst; @@ -3427,21 +3804,21 @@ int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc) u8 buf[1]; if (drm_dp_dpcd_readb(&intel_dp->aux, DP_TEST_SINK_MISC, buf) < 0) - return -EAGAIN; + return -EIO; if (!(buf[0] & DP_TEST_CRC_SUPPORTED)) return -ENOTTY; if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_SINK, DP_TEST_SINK_START) < 0) - return -EAGAIN; + return -EIO; /* Wait 2 vblanks to be sure we will have the correct CRC value */ intel_wait_for_vblank(dev, intel_crtc->pipe); intel_wait_for_vblank(dev, intel_crtc->pipe); if (drm_dp_dpcd_read(&intel_dp->aux, DP_TEST_CRC_R_CR, crc, 6) < 0) - return -EAGAIN; + return -EIO; drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_SINK, 0); return 0; @@ -3643,21 +4020,25 @@ intel_dp_detect_dpcd(struct intel_dp *intel_dp) return connector_status_disconnected; } +static enum drm_connector_status +edp_detect(struct intel_dp *intel_dp) +{ + struct drm_device *dev = intel_dp_to_dev(intel_dp); + enum drm_connector_status status; + + status = intel_panel_detect(dev); + if (status == connector_status_unknown) + status = connector_status_connected; + + return status; +} + static enum drm_connector_status ironlake_dp_detect(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); struct drm_i915_private *dev_priv = dev->dev_private; struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); - enum drm_connector_status status; - - /* Can't disconnect eDP, but you can close the lid... */ - if (is_edp(intel_dp)) { - status = intel_panel_detect(dev); - if (status == connector_status_unknown) - status = connector_status_connected; - return status; - } if (!ibx_digital_port_connected(dev_priv, intel_dig_port)) return connector_status_disconnected; @@ -3733,9 +4114,9 @@ g4x_dp_detect(struct intel_dp *intel_dp) } static struct edid * -intel_dp_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) +intel_dp_get_edid(struct intel_dp *intel_dp) { - struct intel_connector *intel_connector = to_intel_connector(connector); + struct intel_connector *intel_connector = intel_dp->attached_connector; /* use cached edid if we have one */ if (intel_connector->edid) { @@ -3744,27 +4125,55 @@ intel_dp_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) return NULL; return drm_edid_duplicate(intel_connector->edid); - } + } else + return drm_get_edid(&intel_connector->base, + &intel_dp->aux.ddc); +} + +static void +intel_dp_set_edid(struct intel_dp *intel_dp) +{ + struct intel_connector *intel_connector = intel_dp->attached_connector; + struct edid *edid; - return drm_get_edid(connector, adapter); + edid = intel_dp_get_edid(intel_dp); + intel_connector->detect_edid = edid; + + if (intel_dp->force_audio != HDMI_AUDIO_AUTO) + intel_dp->has_audio = intel_dp->force_audio == HDMI_AUDIO_ON; + else + intel_dp->has_audio = drm_detect_monitor_audio(edid); } -static int -intel_dp_get_edid_modes(struct drm_connector *connector, struct i2c_adapter *adapter) +static void +intel_dp_unset_edid(struct intel_dp *intel_dp) { - struct intel_connector *intel_connector = to_intel_connector(connector); + struct intel_connector *intel_connector = intel_dp->attached_connector; - /* use cached edid if we have one */ - if (intel_connector->edid) { - /* invalid edid */ - if (IS_ERR(intel_connector->edid)) - return 0; + kfree(intel_connector->detect_edid); + intel_connector->detect_edid = NULL; - return intel_connector_update_modes(connector, - intel_connector->edid); - } + intel_dp->has_audio = false; +} + +static enum intel_display_power_domain +intel_dp_power_get(struct intel_dp *dp) +{ + struct intel_encoder *encoder = &dp_to_dig_port(dp)->base; + enum intel_display_power_domain power_domain; + + power_domain = intel_display_port_power_domain(encoder); + intel_display_power_get(to_i915(encoder->base.dev), power_domain); + + return power_domain; +} - return intel_ddc_get_modes(connector, adapter); +static void +intel_dp_power_put(struct intel_dp *dp, + enum intel_display_power_domain power_domain) +{ + struct intel_encoder *encoder = &dp_to_dig_port(dp)->base; + intel_display_power_put(to_i915(encoder->base.dev), power_domain); } static enum drm_connector_status @@ -3774,33 +4183,30 @@ intel_dp_detect(struct drm_connector *connector, bool force) struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct intel_encoder *intel_encoder = &intel_dig_port->base; struct drm_device *dev = connector->dev; - struct drm_i915_private *dev_priv = dev->dev_private; enum drm_connector_status status; enum intel_display_power_domain power_domain; - struct edid *edid = NULL; bool ret; - power_domain = intel_display_port_power_domain(intel_encoder); - intel_display_power_get(dev_priv, power_domain); - DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, connector->name); + intel_dp_unset_edid(intel_dp); if (intel_dp->is_mst) { /* MST devices are disconnected from a monitor POV */ if (intel_encoder->type != INTEL_OUTPUT_EDP) intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; - status = connector_status_disconnected; - goto out; + return connector_status_disconnected; } - intel_dp->has_audio = false; + power_domain = intel_dp_power_get(intel_dp); - if (HAS_PCH_SPLIT(dev)) + /* Can't disconnect eDP, but you can close the lid... */ + if (is_edp(intel_dp)) + status = edp_detect(intel_dp); + else if (HAS_PCH_SPLIT(dev)) status = ironlake_dp_detect(intel_dp); else status = g4x_dp_detect(intel_dp); - if (status != connector_status_connected) goto out; @@ -3816,82 +4222,78 @@ intel_dp_detect(struct drm_connector *connector, bool force) goto out; } - if (intel_dp->force_audio != HDMI_AUDIO_AUTO) { - intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON); - } else { - edid = intel_dp_get_edid(connector, &intel_dp->aux.ddc); - if (edid) { - intel_dp->has_audio = drm_detect_monitor_audio(edid); - kfree(edid); - } - } + intel_dp_set_edid(intel_dp); if (intel_encoder->type != INTEL_OUTPUT_EDP) intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; status = connector_status_connected; out: - intel_display_power_put(dev_priv, power_domain); + intel_dp_power_put(intel_dp, power_domain); return status; } -static int intel_dp_get_modes(struct drm_connector *connector) +static void +intel_dp_force(struct drm_connector *connector) { struct intel_dp *intel_dp = intel_attached_dp(connector); - struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); - struct intel_encoder *intel_encoder = &intel_dig_port->base; - struct intel_connector *intel_connector = to_intel_connector(connector); - struct drm_device *dev = connector->dev; - struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base; enum intel_display_power_domain power_domain; - int ret; - /* We should parse the EDID data and find out if it has an audio sink - */ + DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + intel_dp_unset_edid(intel_dp); - power_domain = intel_display_port_power_domain(intel_encoder); - intel_display_power_get(dev_priv, power_domain); + if (connector->status != connector_status_connected) + return; - ret = intel_dp_get_edid_modes(connector, &intel_dp->aux.ddc); - intel_display_power_put(dev_priv, power_domain); - if (ret) - return ret; + power_domain = intel_dp_power_get(intel_dp); + + intel_dp_set_edid(intel_dp); + + intel_dp_power_put(intel_dp, power_domain); + + if (intel_encoder->type != INTEL_OUTPUT_EDP) + intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; +} + +static int intel_dp_get_modes(struct drm_connector *connector) +{ + struct intel_connector *intel_connector = to_intel_connector(connector); + struct edid *edid; + + edid = intel_connector->detect_edid; + if (edid) { + int ret = intel_connector_update_modes(connector, edid); + if (ret) + return ret; + } /* if eDP has no EDID, fall back to fixed mode */ - if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) { + if (is_edp(intel_attached_dp(connector)) && + intel_connector->panel.fixed_mode) { struct drm_display_mode *mode; - mode = drm_mode_duplicate(dev, + + mode = drm_mode_duplicate(connector->dev, intel_connector->panel.fixed_mode); if (mode) { drm_mode_probed_add(connector, mode); return 1; } } + return 0; } static bool intel_dp_detect_audio(struct drm_connector *connector) { - struct intel_dp *intel_dp = intel_attached_dp(connector); - struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); - struct intel_encoder *intel_encoder = &intel_dig_port->base; - struct drm_device *dev = connector->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - enum intel_display_power_domain power_domain; - struct edid *edid; bool has_audio = false; + struct edid *edid; - power_domain = intel_display_port_power_domain(intel_encoder); - intel_display_power_get(dev_priv, power_domain); - - edid = intel_dp_get_edid(connector, &intel_dp->aux.ddc); - if (edid) { + edid = to_intel_connector(connector)->detect_edid; + if (edid) has_audio = drm_detect_monitor_audio(edid); - kfree(edid); - } - - intel_display_power_put(dev_priv, power_domain); return has_audio; } @@ -3989,6 +4391,8 @@ intel_dp_connector_destroy(struct drm_connector *connector) { struct intel_connector *intel_connector = to_intel_connector(connector); + kfree(intel_connector->detect_edid); + if (!IS_ERR_OR_NULL(intel_connector->edid)) kfree(intel_connector->edid); @@ -4005,16 +4409,20 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder) { struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); struct intel_dp *intel_dp = &intel_dig_port->dp; - struct drm_device *dev = intel_dp_to_dev(intel_dp); drm_dp_aux_unregister(&intel_dp->aux); intel_dp_mst_encoder_cleanup(intel_dig_port); drm_encoder_cleanup(encoder); if (is_edp(intel_dp)) { cancel_delayed_work_sync(&intel_dp->panel_vdd_work); - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + /* + * vdd might still be enabled do to the delayed vdd off. + * Make sure vdd is actually turned off here. + */ + pps_lock(intel_dp); edp_panel_vdd_off_sync(intel_dp); - drm_modeset_unlock(&dev->mode_config.connection_mutex); + pps_unlock(intel_dp); + if (intel_dp->edp_notifier.notifier_call) { unregister_reboot_notifier(&intel_dp->edp_notifier); intel_dp->edp_notifier.notifier_call = NULL; @@ -4030,7 +4438,13 @@ static void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder) if (!is_edp(intel_dp)) return; + /* + * vdd might still be enabled do to the delayed vdd off. + * Make sure vdd is actually turned off here. + */ + pps_lock(intel_dp); edp_panel_vdd_off_sync(intel_dp); + pps_unlock(intel_dp); } static void intel_dp_encoder_reset(struct drm_encoder *encoder) @@ -4041,6 +4455,7 @@ static void intel_dp_encoder_reset(struct drm_encoder *encoder) static const struct drm_connector_funcs intel_dp_connector_funcs = { .dpms = intel_connector_dpms, .detect = intel_dp_detect, + .force = intel_dp_force, .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_dp_set_property, .destroy = intel_dp_connector_destroy, @@ -4076,7 +4491,8 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd) if (intel_dig_port->base.type != INTEL_OUTPUT_EDP) intel_dig_port->base.type = INTEL_OUTPUT_DISPLAYPORT; - DRM_DEBUG_KMS("got hpd irq on port %d - %s\n", intel_dig_port->port, + DRM_DEBUG_KMS("got hpd irq on port %c - %s\n", + port_name(intel_dig_port->port), long_hpd ? "long" : "short"); power_domain = intel_display_port_power_domain(intel_encoder); @@ -4216,6 +4632,8 @@ intel_dp_init_panel_power_sequencer(struct drm_device *dev, u32 pp_on, pp_off, pp_div, pp; int pp_ctrl_reg, pp_on_reg, pp_off_reg, pp_div_reg; + lockdep_assert_held(&dev_priv->pps_mutex); + if (HAS_PCH_SPLIT(dev)) { pp_ctrl_reg = PCH_PP_CONTROL; pp_on_reg = PCH_PP_ON_DELAYS; @@ -4315,6 +4733,9 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, u32 pp_on, pp_off, pp_div, port_sel = 0; int div = HAS_PCH_SPLIT(dev) ? intel_pch_rawclk(dev) : intel_hrawclk(dev); int pp_on_reg, pp_off_reg, pp_div_reg; + enum port port = dp_to_dig_port(intel_dp)->port; + + lockdep_assert_held(&dev_priv->pps_mutex); if (HAS_PCH_SPLIT(dev)) { pp_on_reg = PCH_PP_ON_DELAYS; @@ -4349,12 +4770,9 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, /* Haswell doesn't have any port selection bits for the panel * power sequencer any more. */ if (IS_VALLEYVIEW(dev)) { - if (dp_to_dig_port(intel_dp)->port == PORT_B) - port_sel = PANEL_PORT_SELECT_DPB_VLV; - else - port_sel = PANEL_PORT_SELECT_DPC_VLV; + port_sel = PANEL_PORT_SELECT_VLV(port); } else if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { - if (dp_to_dig_port(intel_dp)->port == PORT_A) + if (port == PORT_A) port_sel = PANEL_PORT_SELECT_DPA; else port_sel = PANEL_PORT_SELECT_DPD; @@ -4438,7 +4856,7 @@ void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate) val = I915_READ(reg); if (index > DRRS_HIGH_RR) { val |= PIPECONF_EDP_RR_MODE_SWITCH; - intel_dp_set_m2_n2(intel_crtc, &config->dp_m2_n2); + intel_dp_set_m_n(intel_crtc); } else { val &= ~PIPECONF_EDP_RR_MODE_SWITCH; } @@ -4478,7 +4896,7 @@ intel_dp_drrs_init(struct intel_digital_port *intel_dig_port, } if (dev_priv->vbt.drrs_type != SEAMLESS_DRRS_SUPPORT) { - DRM_INFO("VBT doesn't support DRRS\n"); + DRM_DEBUG_KMS("VBT doesn't support DRRS\n"); return NULL; } @@ -4486,7 +4904,7 @@ intel_dp_drrs_init(struct intel_digital_port *intel_dig_port, (dev, fixed_mode, connector); if (!downclock_mode) { - DRM_INFO("DRRS not supported\n"); + DRM_DEBUG_KMS("DRRS not supported\n"); return NULL; } @@ -4497,7 +4915,7 @@ intel_dp_drrs_init(struct intel_digital_port *intel_dig_port, intel_dp->drrs_state.type = dev_priv->vbt.drrs_type; intel_dp->drrs_state.refresh_rate_type = DRRS_HIGH_RR; - DRM_INFO("seamless DRRS supported for eDP panel.\n"); + DRM_DEBUG_KMS("seamless DRRS supported for eDP panel.\n"); return downclock_mode; } @@ -4512,8 +4930,11 @@ void intel_edp_panel_vdd_sanitize(struct intel_encoder *intel_encoder) return; intel_dp = enc_to_intel_dp(&intel_encoder->base); + + pps_lock(intel_dp); + if (!edp_have_panel_vdd(intel_dp)) - return; + goto out; /* * The VDD bit needs a power domain reference, so if the bit is * already enabled when we boot or resume, grab this reference and @@ -4525,6 +4946,8 @@ void intel_edp_panel_vdd_sanitize(struct intel_encoder *intel_encoder) intel_display_power_get(dev_priv, power_domain); edp_panel_vdd_schedule_off(intel_dp); + out: + pps_unlock(intel_dp); } static bool intel_edp_init_connector(struct intel_dp *intel_dp, @@ -4552,7 +4975,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, /* Cache DPCD and EDID for edp. */ intel_edp_panel_vdd_on(intel_dp); has_dpcd = intel_dp_get_dpcd(intel_dp); - edp_panel_vdd_off(intel_dp, false); + intel_edp_panel_vdd_off(intel_dp, false); if (has_dpcd) { if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) @@ -4566,7 +4989,9 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, } /* We now know it's not a ghost, init power sequence regs. */ + pps_lock(intel_dp); intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, power_seq); + pps_unlock(intel_dp); mutex_lock(&dev->mode_config.mutex); edid = drm_get_edid(connector, &intel_dp->aux.ddc); @@ -4610,6 +5035,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, } intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode); + intel_connector->panel.backlight_power = intel_edp_backlight_power; intel_panel_setup_backlight(connector); return true; @@ -4628,6 +5054,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, struct edp_power_seq power_seq = { 0 }; int type; + intel_dp->pps_pipe = INVALID_PIPE; + /* intel_dp vfuncs */ if (IS_VALLEYVIEW(dev)) intel_dp->get_aux_clock_divider = vlv_get_aux_clock_divider; @@ -4698,8 +5126,15 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, } if (is_edp(intel_dp)) { - intel_dp_init_panel_power_timestamps(intel_dp); - intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); + pps_lock(intel_dp); + if (IS_VALLEYVIEW(dev)) { + vlv_initial_power_sequencer_setup(intel_dp); + } else { + intel_dp_init_panel_power_timestamps(intel_dp); + intel_dp_init_panel_power_sequencer(dev, intel_dp, + &power_seq); + } + pps_unlock(intel_dp); } intel_dp_aux_init(intel_dp, intel_connector); @@ -4707,7 +5142,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, /* init MST on ports that can support it */ if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { if (port == PORT_B || port == PORT_C || port == PORT_D) { - intel_dp_mst_encoder_init(intel_dig_port, intel_connector->base.base.id); + intel_dp_mst_encoder_init(intel_dig_port, + intel_connector->base.base.id); } } @@ -4715,9 +5151,13 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, drm_dp_aux_unregister(&intel_dp->aux); if (is_edp(intel_dp)) { cancel_delayed_work_sync(&intel_dp->panel_vdd_work); - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + /* + * vdd might still be enabled do to the delayed vdd off. + * Make sure vdd is actually turned off here. + */ + pps_lock(intel_dp); edp_panel_vdd_off_sync(intel_dp); - drm_modeset_unlock(&dev->mode_config.connection_mutex); + pps_unlock(intel_dp); } drm_connector_unregister(connector); drm_connector_cleanup(connector); @@ -4781,7 +5221,8 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) } else { intel_encoder->pre_enable = g4x_pre_enable_dp; intel_encoder->enable = g4x_enable_dp; - intel_encoder->post_disable = g4x_post_disable_dp; + if (INTEL_INFO(dev)->gen >= 5) + intel_encoder->post_disable = ilk_post_disable_dp; } intel_dig_port->port = port; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index b8c8bbd8e5f990b256ffe20f853395dbe6e784a2..07ce04683c3091a11afeacdee99e3b1b41c03461 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -25,6 +25,7 @@ #ifndef __INTEL_DRV_H__ #define __INTEL_DRV_H__ +#include #include #include #include @@ -179,6 +180,8 @@ struct intel_panel { bool active_low_pwm; struct backlight_device *device; } backlight; + + void (*backlight_power)(struct intel_connector *, bool enable); }; struct intel_connector { @@ -211,6 +214,7 @@ struct intel_connector { /* Cached EDID for eDP and LVDS. May hold ERR_PTR for invalid EDID. */ struct edid *edid; + struct edid *detect_edid; /* since POLL and HPD connectors may use the same HPD line keep the native state of connector->polled in case hotplug storm detection changes it */ @@ -330,6 +334,7 @@ struct intel_crtc_config { /* m2_n2 for eDP downclock */ struct intel_link_m_n dp_m2_n2; + bool has_drrs; /* * Frequence the dpll for the port should run at. Differs from the @@ -410,6 +415,7 @@ struct intel_crtc { uint32_t cursor_addr; int16_t cursor_width, cursor_height; uint32_t cursor_cntl; + uint32_t cursor_size; uint32_t cursor_base; struct intel_plane_config plane_config; @@ -430,8 +436,6 @@ struct intel_crtc { struct intel_pipe_wm active; } wm; - wait_queue_head_t vbl_wait; - int scanline_offset; struct intel_mmio_flip mmio_flip; }; @@ -455,6 +459,7 @@ struct intel_plane { unsigned int crtc_w, crtc_h; uint32_t src_x, src_y; uint32_t src_w, src_h; + unsigned int rotation; /* Since we need to change the watermarks before/after * enabling/disabling the planes, we need to store the parameters here @@ -565,6 +570,12 @@ struct intel_dp { struct notifier_block edp_notifier; + /* + * Pipe whose power sequencer is currently locked into + * this port. Only relevant on VLV/CHV. + */ + enum pipe pps_pipe; + bool use_tps3; bool can_mst; /* this port supports mst */ bool is_mst; @@ -663,6 +674,10 @@ struct intel_unpin_work { #define INTEL_FLIP_COMPLETE 2 u32 flip_count; u32 gtt_offset; + struct intel_engine_cs *flip_queued_ring; + u32 flip_queued_seqno; + int flip_queued_vblank; + int flip_ready_vblank; bool enable_stall_check; }; @@ -827,7 +842,6 @@ int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, enum transcoder intel_pipe_to_cpu_transcoder(struct drm_i915_private *dev_priv, enum pipe pipe); void intel_wait_for_vblank(struct drm_device *dev, int pipe); -void intel_wait_for_pipe_off(struct drm_device *dev, int pipe); int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp); void vlv_wait_port_ready(struct drm_i915_private *dev_priv, struct intel_digital_port *dport); @@ -848,6 +862,7 @@ __intel_framebuffer_create(struct drm_device *dev, void intel_prepare_page_flip(struct drm_device *dev, int plane); void intel_finish_page_flip(struct drm_device *dev, int pipe); void intel_finish_page_flip_plane(struct drm_device *dev, int plane); +void intel_check_page_flip(struct drm_device *dev, int pipe); /* shared dpll functions */ struct intel_shared_dpll *intel_crtc_to_shared_dpll(struct intel_crtc *crtc); @@ -882,6 +897,7 @@ void hsw_enable_pc8(struct drm_i915_private *dev_priv); void hsw_disable_pc8(struct drm_i915_private *dev_priv); void intel_dp_get_m_n(struct intel_crtc *crtc, struct intel_crtc_config *pipe_config); +void intel_dp_set_m_n(struct intel_crtc *crtc); int intel_dotclock_calculate(int link_freq, const struct intel_link_m_n *m_n); void ironlake_check_encoder_dotclock(const struct intel_crtc_config *pipe_config, @@ -896,7 +912,7 @@ void intel_mode_from_pipe_config(struct drm_display_mode *mode, struct intel_crtc_config *pipe_config); int intel_format_to_fourcc(int format); void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc); - +void intel_modeset_preclose(struct drm_device *dev, struct drm_file *file); /* intel_dp.c */ void intel_dp_init(struct drm_device *dev, int output_reg, enum port port); @@ -935,6 +951,7 @@ void intel_dp_mst_suspend(struct drm_device *dev); void intel_dp_mst_resume(struct drm_device *dev); int intel_dp_max_link_bw(struct intel_dp *intel_dp); void intel_dp_hot_plug(struct intel_encoder *intel_encoder); +void vlv_power_sequencer_reset(struct drm_i915_private *dev_priv); /* intel_dp_mst.c */ int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id); void intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port); @@ -949,9 +966,9 @@ void intel_dvo_init(struct drm_device *dev); /* legacy fbdev emulation in intel_fbdev.c */ #ifdef CONFIG_DRM_I915_FBDEV extern int intel_fbdev_init(struct drm_device *dev); -extern void intel_fbdev_initial_config(struct drm_device *dev); +extern void intel_fbdev_initial_config(void *data, async_cookie_t cookie); extern void intel_fbdev_fini(struct drm_device *dev); -extern void intel_fbdev_set_suspend(struct drm_device *dev, int state); +extern void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous); extern void intel_fbdev_output_poll_changed(struct drm_device *dev); extern void intel_fbdev_restore_mode(struct drm_device *dev); #else @@ -960,7 +977,7 @@ static inline int intel_fbdev_init(struct drm_device *dev) return 0; } -static inline void intel_fbdev_initial_config(struct drm_device *dev) +static inline void intel_fbdev_initial_config(void *data, async_cookie_t cookie) { } @@ -968,7 +985,7 @@ static inline void intel_fbdev_fini(struct drm_device *dev) { } -static inline void intel_fbdev_set_suspend(struct drm_device *dev, int state) +static inline void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous) { } @@ -1091,7 +1108,10 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob); int intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane); void intel_flush_primary_plane(struct drm_i915_private *dev_priv, enum plane plane); -void intel_plane_restore(struct drm_plane *plane); +int intel_plane_set_property(struct drm_plane *plane, + struct drm_property *prop, + uint64_t val); +int intel_plane_restore(struct drm_plane *plane); void intel_plane_disable(struct drm_plane *plane); int intel_sprite_set_colorkey(struct drm_device *dev, void *data, struct drm_file *file_priv); diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index 670c29a7b5dd8e2a10867be9d742ace0e6ad3e11..5bd9e09ad3c5ddac4202a6f21e6fb7198572ffb2 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -184,7 +184,7 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder) /* update the hw state for DPLL */ intel_crtc->config.dpll_hw_state.dpll = DPLL_INTEGRATED_CLOCK_VLV | - DPLL_REFA_CLK_ENABLE_VLV; + DPLL_REFA_CLK_ENABLE_VLV; tmp = I915_READ(DSPCLK_GATE_D); tmp |= DPOUNIT_CLOCK_GATE_DISABLE; @@ -259,8 +259,8 @@ static void intel_dsi_disable(struct intel_encoder *encoder) temp = I915_READ(MIPI_CTRL(pipe)); temp &= ~ESCAPE_CLOCK_DIVIDER_MASK; I915_WRITE(MIPI_CTRL(pipe), temp | - intel_dsi->escape_clk_div << - ESCAPE_CLOCK_DIVIDER_SHIFT); + intel_dsi->escape_clk_div << + ESCAPE_CLOCK_DIVIDER_SHIFT); I915_WRITE(MIPI_EOT_DISABLE(pipe), CLOCKSTOP); @@ -297,7 +297,7 @@ static void intel_dsi_clear_device_ready(struct intel_encoder *encoder) usleep_range(2000, 2500); if (wait_for(((I915_READ(MIPI_PORT_CTRL(pipe)) & AFE_LATCHOUT) - == 0x00000), 30)) + == 0x00000), 30)) DRM_ERROR("DSI LP not going Low\n"); val = I915_READ(MIPI_PORT_CTRL(pipe)); @@ -423,9 +423,11 @@ static u16 txclkesc(u32 divider, unsigned int us) } /* return pixels in terms of txbyteclkhs */ -static u16 txbyteclkhs(u16 pixels, int bpp, int lane_count) +static u16 txbyteclkhs(u16 pixels, int bpp, int lane_count, + u16 burst_mode_ratio) { - return DIV_ROUND_UP(DIV_ROUND_UP(pixels * bpp, 8), lane_count); + return DIV_ROUND_UP(DIV_ROUND_UP(pixels * bpp * burst_mode_ratio, + 8 * 100), lane_count); } static void set_dsi_timings(struct drm_encoder *encoder, @@ -451,10 +453,12 @@ static void set_dsi_timings(struct drm_encoder *encoder, vbp = mode->vtotal - mode->vsync_end; /* horizontal values are in terms of high speed byte clock */ - hactive = txbyteclkhs(hactive, bpp, lane_count); - hfp = txbyteclkhs(hfp, bpp, lane_count); - hsync = txbyteclkhs(hsync, bpp, lane_count); - hbp = txbyteclkhs(hbp, bpp, lane_count); + hactive = txbyteclkhs(hactive, bpp, lane_count, + intel_dsi->burst_mode_ratio); + hfp = txbyteclkhs(hfp, bpp, lane_count, intel_dsi->burst_mode_ratio); + hsync = txbyteclkhs(hsync, bpp, lane_count, + intel_dsi->burst_mode_ratio); + hbp = txbyteclkhs(hbp, bpp, lane_count, intel_dsi->burst_mode_ratio); I915_WRITE(MIPI_HACTIVE_AREA_COUNT(pipe), hactive); I915_WRITE(MIPI_HFP_COUNT(pipe), hfp); @@ -541,12 +545,14 @@ static void intel_dsi_prepare(struct intel_encoder *intel_encoder) intel_dsi->video_mode_format == VIDEO_MODE_BURST) { I915_WRITE(MIPI_HS_TX_TIMEOUT(pipe), txbyteclkhs(adjusted_mode->htotal, bpp, - intel_dsi->lane_count) + 1); + intel_dsi->lane_count, + intel_dsi->burst_mode_ratio) + 1); } else { I915_WRITE(MIPI_HS_TX_TIMEOUT(pipe), txbyteclkhs(adjusted_mode->vtotal * adjusted_mode->htotal, - bpp, intel_dsi->lane_count) + 1); + bpp, intel_dsi->lane_count, + intel_dsi->burst_mode_ratio) + 1); } I915_WRITE(MIPI_LP_RX_TIMEOUT(pipe), intel_dsi->lp_rx_timeout); I915_WRITE(MIPI_TURN_AROUND_TIMEOUT(pipe), intel_dsi->turn_arnd_val); @@ -576,7 +582,7 @@ static void intel_dsi_prepare(struct intel_encoder *intel_encoder) * XXX: write MIPI_STOP_STATE_STALL? */ I915_WRITE(MIPI_HIGH_LOW_SWITCH_COUNT(pipe), - intel_dsi->hs_to_lp_count); + intel_dsi->hs_to_lp_count); /* XXX: low power clock equivalence in terms of byte clock. the number * of byte clocks occupied in one low power clock. based on txbyteclkhs @@ -601,10 +607,10 @@ static void intel_dsi_prepare(struct intel_encoder *intel_encoder) * 64 like 1366 x 768. Enable RANDOM resolution support for such * panels by default */ I915_WRITE(MIPI_VIDEO_MODE_FORMAT(pipe), - intel_dsi->video_frmt_cfg_bits | - intel_dsi->video_mode_format | - IP_TG_CONFIG | - RANDOM_DPI_DISPLAY_RESOLUTION); + intel_dsi->video_frmt_cfg_bits | + intel_dsi->video_mode_format | + IP_TG_CONFIG | + RANDOM_DPI_DISPLAY_RESOLUTION); } static void intel_dsi_pre_pll_enable(struct intel_encoder *encoder) diff --git a/drivers/gpu/drm/i915/intel_dsi.h b/drivers/gpu/drm/i915/intel_dsi.h index fd51867fd0d3157ef849f4f50367e92865932f77..657eb5c1b9d80db7993a28febb8847c2dcf8665f 100644 --- a/drivers/gpu/drm/i915/intel_dsi.h +++ b/drivers/gpu/drm/i915/intel_dsi.h @@ -116,6 +116,8 @@ struct intel_dsi { u16 clk_hs_to_lp_count; u16 init_count; + u32 pclk; + u16 burst_mode_ratio; /* all delays in ms */ u16 backlight_off_delay; diff --git a/drivers/gpu/drm/i915/intel_dsi_cmd.c b/drivers/gpu/drm/i915/intel_dsi_cmd.c index 7f1430ac8543617e5cc1788c98be97836ef85771..f4767fd2ebeb0ea9d60c971de76a164c459a5ef8 100644 --- a/drivers/gpu/drm/i915/intel_dsi_cmd.c +++ b/drivers/gpu/drm/i915/intel_dsi_cmd.c @@ -430,7 +430,7 @@ void wait_for_dsi_fifo_empty(struct intel_dsi *intel_dsi) u32 mask; mask = LP_CTRL_FIFO_EMPTY | HS_CTRL_FIFO_EMPTY | - LP_DATA_FIFO_EMPTY | HS_DATA_FIFO_EMPTY; + LP_DATA_FIFO_EMPTY | HS_DATA_FIFO_EMPTY; if (wait_for((I915_READ(MIPI_GEN_FIFO_STAT(pipe)) & mask) == mask, 100)) DRM_ERROR("DPI FIFOs are not empty\n"); diff --git a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c index 47c7584a4aa0336938bb1150cc141e0e1063bfee..f6bdd44069cec77c676b2ac7222642cb5ed76f32 100644 --- a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c +++ b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c @@ -271,6 +271,8 @@ static bool generic_init(struct intel_dsi_device *dsi) u32 ths_prepare_ns, tclk_trail_ns; u32 tclk_prepare_clkzero, ths_prepare_hszero; u32 lp_to_hs_switch, hs_to_lp_switch; + u32 pclk, computed_ddr; + u16 burst_mode_ratio; DRM_DEBUG_KMS("\n"); @@ -284,8 +286,6 @@ static bool generic_init(struct intel_dsi_device *dsi) else if (intel_dsi->pixel_format == VID_MODE_FORMAT_RGB565) bits_per_pixel = 16; - bitrate = (mode->clock * bits_per_pixel) / intel_dsi->lane_count; - intel_dsi->operation_mode = mipi_config->is_cmd_mode; intel_dsi->video_mode_format = mipi_config->video_transfer_mode; intel_dsi->escape_clk_div = mipi_config->byte_clk_sel; @@ -297,6 +297,40 @@ static bool generic_init(struct intel_dsi_device *dsi) intel_dsi->video_frmt_cfg_bits = mipi_config->bta_enabled ? DISABLE_VIDEO_BTA : 0; + pclk = mode->clock; + + /* Burst Mode Ratio + * Target ddr frequency from VBT / non burst ddr freq + * multiply by 100 to preserve remainder + */ + if (intel_dsi->video_mode_format == VIDEO_MODE_BURST) { + if (mipi_config->target_burst_mode_freq) { + computed_ddr = + (pclk * bits_per_pixel) / intel_dsi->lane_count; + + if (mipi_config->target_burst_mode_freq < + computed_ddr) { + DRM_ERROR("Burst mode freq is less than computed\n"); + return false; + } + + burst_mode_ratio = DIV_ROUND_UP( + mipi_config->target_burst_mode_freq * 100, + computed_ddr); + + pclk = DIV_ROUND_UP(pclk * burst_mode_ratio, 100); + } else { + DRM_ERROR("Burst mode target is not set\n"); + return false; + } + } else + burst_mode_ratio = 100; + + intel_dsi->burst_mode_ratio = burst_mode_ratio; + intel_dsi->pclk = pclk; + + bitrate = (pclk * bits_per_pixel) / intel_dsi->lane_count; + switch (intel_dsi->escape_clk_div) { case 0: tlpx_ns = 50; diff --git a/drivers/gpu/drm/i915/intel_dsi_pll.c b/drivers/gpu/drm/i915/intel_dsi_pll.c index d8bb1ea2f0da5a4be3a7b097cfe9b79bad954795..fa7a6ca34cd654bb7c66665c4c80235b28ea2673 100644 --- a/drivers/gpu/drm/i915/intel_dsi_pll.c +++ b/drivers/gpu/drm/i915/intel_dsi_pll.c @@ -134,8 +134,7 @@ static u32 dsi_rr_formula(const struct drm_display_mode *mode, #else /* Get DSI clock from pixel clock */ -static u32 dsi_clk_from_pclk(const struct drm_display_mode *mode, - int pixel_format, int lane_count) +static u32 dsi_clk_from_pclk(u32 pclk, int pixel_format, int lane_count) { u32 dsi_clk_khz; u32 bpp; @@ -156,7 +155,7 @@ static u32 dsi_clk_from_pclk(const struct drm_display_mode *mode, /* DSI data rate = pixel clock * bits per pixel / lane count pixel clock is converted from KHz to Hz */ - dsi_clk_khz = DIV_ROUND_CLOSEST(mode->clock * bpp, lane_count); + dsi_clk_khz = DIV_ROUND_CLOSEST(pclk * bpp, lane_count); return dsi_clk_khz; } @@ -191,7 +190,7 @@ static int dsi_calc_mnp(u32 dsi_clk, struct dsi_mnp *dsi_mnp) for (m = 62; m <= 92; m++) { for (p = 2; p <= 6; p++) { /* Find the optimal m and p divisors - with minimal error +/- the required clock */ + with minimal error +/- the required clock */ calc_dsi_clk = (m * ref_clk) / p; if (calc_dsi_clk == target_dsi_clk) { calc_m = m; @@ -228,15 +227,13 @@ static int dsi_calc_mnp(u32 dsi_clk, struct dsi_mnp *dsi_mnp) static void vlv_configure_dsi_pll(struct intel_encoder *encoder) { struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); - const struct drm_display_mode *mode = &intel_crtc->config.adjusted_mode; struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); int ret; struct dsi_mnp dsi_mnp; u32 dsi_clk; - dsi_clk = dsi_clk_from_pclk(mode, intel_dsi->pixel_format, - intel_dsi->lane_count); + dsi_clk = dsi_clk_from_pclk(intel_dsi->pclk, intel_dsi->pixel_format, + intel_dsi->lane_count); ret = dsi_calc_mnp(dsi_clk, &dsi_mnp); if (ret) { @@ -318,8 +315,8 @@ static void assert_bpp_mismatch(int pixel_format, int pipe_bpp) } WARN(bpp != pipe_bpp, - "bpp match assertion failure (expected %d, current %d)\n", - bpp, pipe_bpp); + "bpp match assertion failure (expected %d, current %d)\n", + bpp, pipe_bpp); } u32 vlv_get_dsi_pclk(struct intel_encoder *encoder, int pipe_bpp) diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 56b47d2ffaf7ea0227fced60ee4ae8979e9803ec..e40e3df335179979c763123b866386af4ae75eb8 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -85,7 +85,7 @@ static const struct intel_dvo_device intel_dvo_devices[] = { { .type = INTEL_DVO_CHIP_TMDS, .name = "ns2501", - .dvo_reg = DVOC, + .dvo_reg = DVOB, .slave_addr = NS2501_ADDR, .dev_ops = &ns2501_ops, } @@ -185,12 +185,13 @@ static void intel_enable_dvo(struct intel_encoder *encoder) u32 dvo_reg = intel_dvo->dev.dvo_reg; u32 temp = I915_READ(dvo_reg); - I915_WRITE(dvo_reg, temp | DVO_ENABLE); - I915_READ(dvo_reg); intel_dvo->dev.dev_ops->mode_set(&intel_dvo->dev, &crtc->config.requested_mode, &crtc->config.adjusted_mode); + I915_WRITE(dvo_reg, temp | DVO_ENABLE); + I915_READ(dvo_reg); + intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true); } @@ -226,10 +227,6 @@ static void intel_dvo_dpms(struct drm_connector *connector, int mode) intel_crtc_update_dpms(crtc); - intel_dvo->dev.dev_ops->mode_set(&intel_dvo->dev, - &config->requested_mode, - &config->adjusted_mode); - intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true); } else { intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, false); diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index f475414671d8bdd5752a41fa8f4f6339470e365c..9b584f3fbb9912808709bf13ccb002b188eb1c0f 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -24,8 +24,10 @@ * David Airlie */ +#include #include #include +#include #include #include #include @@ -331,24 +333,6 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, int num_connectors_enabled = 0; int num_connectors_detected = 0; - /* - * If the user specified any force options, just bail here - * and use that config. - */ - for (i = 0; i < fb_helper->connector_count; i++) { - struct drm_fb_helper_connector *fb_conn; - struct drm_connector *connector; - - fb_conn = fb_helper->connector_info[i]; - connector = fb_conn->connector; - - if (!enabled[i]) - continue; - - if (connector->force != DRM_FORCE_UNSPECIFIED) - return false; - } - save_enabled = kcalloc(dev->mode_config.num_connector, sizeof(bool), GFP_KERNEL); if (!save_enabled) @@ -374,8 +358,18 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, continue; } + if (connector->force == DRM_FORCE_OFF) { + DRM_DEBUG_KMS("connector %s is disabled by user, skipping\n", + connector->name); + enabled[i] = false; + continue; + } + encoder = connector->encoder; if (!encoder || WARN_ON(!encoder->crtc)) { + if (connector->force > DRM_FORCE_OFF) + goto bail; + DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n", connector->name); enabled[i] = false; @@ -394,8 +388,7 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, for (j = 0; j < fb_helper->connector_count; j++) { if (crtcs[j] == new_crtc) { DRM_DEBUG_KMS("fallback: cloned configuration\n"); - fallback = true; - goto out; + goto bail; } } @@ -466,8 +459,8 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, fallback = true; } -out: if (fallback) { +bail: DRM_DEBUG_KMS("Not using firmware configuration\n"); memcpy(enabled, save_enabled, dev->mode_config.num_connector); kfree(save_enabled); @@ -636,6 +629,15 @@ static bool intel_fbdev_init_bios(struct drm_device *dev, return false; } +static void intel_fbdev_suspend_worker(struct work_struct *work) +{ + intel_fbdev_set_suspend(container_of(work, + struct drm_i915_private, + fbdev_suspend_work)->dev, + FBINFO_STATE_RUNNING, + true); +} + int intel_fbdev_init(struct drm_device *dev) { struct intel_fbdev *ifbdev; @@ -662,14 +664,16 @@ int intel_fbdev_init(struct drm_device *dev) } dev_priv->fbdev = ifbdev; + INIT_WORK(&dev_priv->fbdev_suspend_work, intel_fbdev_suspend_worker); + drm_fb_helper_single_add_all_connectors(&ifbdev->helper); return 0; } -void intel_fbdev_initial_config(struct drm_device *dev) +void intel_fbdev_initial_config(void *data, async_cookie_t cookie) { - struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = data; struct intel_fbdev *ifbdev = dev_priv->fbdev; /* Due to peculiar init order wrt to hpd handling this is separate. */ @@ -682,12 +686,15 @@ void intel_fbdev_fini(struct drm_device *dev) if (!dev_priv->fbdev) return; + flush_work(&dev_priv->fbdev_suspend_work); + + async_synchronize_full(); intel_fbdev_destroy(dev, dev_priv->fbdev); kfree(dev_priv->fbdev); dev_priv->fbdev = NULL; } -void intel_fbdev_set_suspend(struct drm_device *dev, int state) +void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_fbdev *ifbdev = dev_priv->fbdev; @@ -698,6 +705,33 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state) info = ifbdev->helper.fbdev; + if (synchronous) { + /* Flush any pending work to turn the console on, and then + * wait to turn it off. It must be synchronous as we are + * about to suspend or unload the driver. + * + * Note that from within the work-handler, we cannot flush + * ourselves, so only flush outstanding work upon suspend! + */ + if (state != FBINFO_STATE_RUNNING) + flush_work(&dev_priv->fbdev_suspend_work); + console_lock(); + } else { + /* + * The console lock can be pretty contented on resume due + * to all the printk activity. Try to keep it out of the hot + * path of resume if possible. + */ + WARN_ON(state != FBINFO_STATE_RUNNING); + if (!console_trylock()) { + /* Don't block our own workqueue as this can + * be run in parallel with other i915.ko tasks. + */ + schedule_work(&dev_priv->fbdev_suspend_work); + return; + } + } + /* On resume from hibernation: If the object is shmemfs backed, it has * been restored from swap. If the object is stolen however, it will be * full of whatever garbage was left in there. @@ -706,6 +740,7 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state) memset_io(info->screen_base, 0, info->screen_size); fb_set_suspend(info, state); + console_unlock(); } void intel_fbdev_output_poll_changed(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 5a9de21637b7819818e1993f911432e0fdb634de..29ec1535992d413fa5faf70c8192c02d6334b02c 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -869,10 +869,15 @@ static enum drm_mode_status intel_hdmi_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - if (mode->clock > hdmi_portclock_limit(intel_attached_hdmi(connector), - true)) + int clock = mode->clock; + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + clock *= 2; + + if (clock > hdmi_portclock_limit(intel_attached_hdmi(connector), + true)) return MODE_CLOCK_HIGH; - if (mode->clock < 20000) + if (clock < 20000) return MODE_CLOCK_LOW; if (mode->flags & DRM_MODE_FLAG_DBLSCAN) @@ -890,7 +895,7 @@ static bool hdmi_12bpc_possible(struct intel_crtc *crtc) if (HAS_GMCH_DISPLAY(dev)) return false; - list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { + for_each_intel_encoder(dev, encoder) { if (encoder->new_crtc != crtc) continue; @@ -926,6 +931,10 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, intel_hdmi->color_range = 0; } + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK) { + pipe_config->pixel_multiplier = 2; + } + if (intel_hdmi->color_range) pipe_config->limited_color_range = true; @@ -967,104 +976,117 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, return true; } -static enum drm_connector_status -intel_hdmi_detect(struct drm_connector *connector, bool force) +static void +intel_hdmi_unset_edid(struct drm_connector *connector) { - struct drm_device *dev = connector->dev; struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); - struct intel_digital_port *intel_dig_port = - hdmi_to_dig_port(intel_hdmi); - struct intel_encoder *intel_encoder = &intel_dig_port->base; - struct drm_i915_private *dev_priv = dev->dev_private; - struct edid *edid; - enum intel_display_power_domain power_domain; - enum drm_connector_status status = connector_status_disconnected; - DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", - connector->base.id, connector->name); + intel_hdmi->has_hdmi_sink = false; + intel_hdmi->has_audio = false; + intel_hdmi->rgb_quant_range_selectable = false; + + kfree(to_intel_connector(connector)->detect_edid); + to_intel_connector(connector)->detect_edid = NULL; +} + +static bool +intel_hdmi_set_edid(struct drm_connector *connector) +{ + struct drm_i915_private *dev_priv = to_i915(connector->dev); + struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); + struct intel_encoder *intel_encoder = + &hdmi_to_dig_port(intel_hdmi)->base; + enum intel_display_power_domain power_domain; + struct edid *edid; + bool connected = false; power_domain = intel_display_port_power_domain(intel_encoder); intel_display_power_get(dev_priv, power_domain); - intel_hdmi->has_hdmi_sink = false; - intel_hdmi->has_audio = false; - intel_hdmi->rgb_quant_range_selectable = false; edid = drm_get_edid(connector, intel_gmbus_get_adapter(dev_priv, intel_hdmi->ddc_bus)); - if (edid) { - if (edid->input & DRM_EDID_INPUT_DIGITAL) { - status = connector_status_connected; - if (intel_hdmi->force_audio != HDMI_AUDIO_OFF_DVI) - intel_hdmi->has_hdmi_sink = - drm_detect_hdmi_monitor(edid); - intel_hdmi->has_audio = drm_detect_monitor_audio(edid); - intel_hdmi->rgb_quant_range_selectable = - drm_rgb_quant_range_selectable(edid); - } - kfree(edid); - } + intel_display_power_put(dev_priv, power_domain); + + to_intel_connector(connector)->detect_edid = edid; + if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) { + intel_hdmi->rgb_quant_range_selectable = + drm_rgb_quant_range_selectable(edid); - if (status == connector_status_connected) { + intel_hdmi->has_audio = drm_detect_monitor_audio(edid); if (intel_hdmi->force_audio != HDMI_AUDIO_AUTO) intel_hdmi->has_audio = - (intel_hdmi->force_audio == HDMI_AUDIO_ON); - intel_encoder->type = INTEL_OUTPUT_HDMI; + intel_hdmi->force_audio == HDMI_AUDIO_ON; + + if (intel_hdmi->force_audio != HDMI_AUDIO_OFF_DVI) + intel_hdmi->has_hdmi_sink = + drm_detect_hdmi_monitor(edid); + + connected = true; } - intel_display_power_put(dev_priv, power_domain); + return connected; +} + +static enum drm_connector_status +intel_hdmi_detect(struct drm_connector *connector, bool force) +{ + enum drm_connector_status status; + + DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + + intel_hdmi_unset_edid(connector); + + if (intel_hdmi_set_edid(connector)) { + struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); + + hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI; + status = connector_status_connected; + } else + status = connector_status_disconnected; return status; } -static int intel_hdmi_get_modes(struct drm_connector *connector) +static void +intel_hdmi_force(struct drm_connector *connector) { - struct intel_encoder *intel_encoder = intel_attached_encoder(connector); - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base); - struct drm_i915_private *dev_priv = connector->dev->dev_private; - enum intel_display_power_domain power_domain; - int ret; + struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); - /* We should parse the EDID data and find out if it's an HDMI sink so - * we can send audio to it. - */ + DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); - power_domain = intel_display_port_power_domain(intel_encoder); - intel_display_power_get(dev_priv, power_domain); + intel_hdmi_unset_edid(connector); - ret = intel_ddc_get_modes(connector, - intel_gmbus_get_adapter(dev_priv, - intel_hdmi->ddc_bus)); + if (connector->status != connector_status_connected) + return; - intel_display_power_put(dev_priv, power_domain); + intel_hdmi_set_edid(connector); + hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI; +} - return ret; +static int intel_hdmi_get_modes(struct drm_connector *connector) +{ + struct edid *edid; + + edid = to_intel_connector(connector)->detect_edid; + if (edid == NULL) + return 0; + + return intel_connector_update_modes(connector, edid); } static bool intel_hdmi_detect_audio(struct drm_connector *connector) { - struct intel_encoder *intel_encoder = intel_attached_encoder(connector); - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base); - struct drm_i915_private *dev_priv = connector->dev->dev_private; - enum intel_display_power_domain power_domain; - struct edid *edid; bool has_audio = false; + struct edid *edid; - power_domain = intel_display_port_power_domain(intel_encoder); - intel_display_power_get(dev_priv, power_domain); - - edid = drm_get_edid(connector, - intel_gmbus_get_adapter(dev_priv, - intel_hdmi->ddc_bus)); - if (edid) { - if (edid->input & DRM_EDID_INPUT_DIGITAL) - has_audio = drm_detect_monitor_audio(edid); - kfree(edid); - } - - intel_display_power_put(dev_priv, power_domain); + edid = to_intel_connector(connector)->detect_edid; + if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) + has_audio = drm_detect_monitor_audio(edid); return has_audio; } @@ -1265,6 +1287,8 @@ static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder) enum pipe pipe = intel_crtc->pipe; u32 val; + intel_hdmi_prepare(encoder); + mutex_lock(&dev_priv->dpio_lock); /* program left/right clock distribution */ @@ -1434,8 +1458,8 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder) for (i = 0; i < 4; i++) { val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i)); - val &= ~DPIO_SWING_MARGIN_MASK; - val |= 102 << DPIO_SWING_MARGIN_SHIFT; + val &= ~DPIO_SWING_MARGIN000_MASK; + val |= 102 << DPIO_SWING_MARGIN000_SHIFT; vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val); } @@ -1482,6 +1506,7 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder) static void intel_hdmi_destroy(struct drm_connector *connector) { + kfree(to_intel_connector(connector)->detect_edid); drm_connector_cleanup(connector); kfree(connector); } @@ -1489,6 +1514,7 @@ static void intel_hdmi_destroy(struct drm_connector *connector) static const struct drm_connector_funcs intel_hdmi_connector_funcs = { .dpms = intel_connector_dpms, .detect = intel_hdmi_detect, + .force = intel_hdmi_force, .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_hdmi_set_property, .destroy = intel_hdmi_destroy, diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c new file mode 100644 index 0000000000000000000000000000000000000000..bafd38b5703eba00e2ca3c2ffb00fea7840b4638 --- /dev/null +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -0,0 +1,1766 @@ +/* + * Copyright © 2014 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Ben Widawsky + * Michel Thierry + * Thomas Daniel + * Oscar Mateo + * + */ + +/** + * DOC: Logical Rings, Logical Ring Contexts and Execlists + * + * Motivation: + * GEN8 brings an expansion of the HW contexts: "Logical Ring Contexts". + * These expanded contexts enable a number of new abilities, especially + * "Execlists" (also implemented in this file). + * + * One of the main differences with the legacy HW contexts is that logical + * ring contexts incorporate many more things to the context's state, like + * PDPs or ringbuffer control registers: + * + * The reason why PDPs are included in the context is straightforward: as + * PPGTTs (per-process GTTs) are actually per-context, having the PDPs + * contained there mean you don't need to do a ppgtt->switch_mm yourself, + * instead, the GPU will do it for you on the context switch. + * + * But, what about the ringbuffer control registers (head, tail, etc..)? + * shouldn't we just need a set of those per engine command streamer? This is + * where the name "Logical Rings" starts to make sense: by virtualizing the + * rings, the engine cs shifts to a new "ring buffer" with every context + * switch. When you want to submit a workload to the GPU you: A) choose your + * context, B) find its appropriate virtualized ring, C) write commands to it + * and then, finally, D) tell the GPU to switch to that context. + * + * Instead of the legacy MI_SET_CONTEXT, the way you tell the GPU to switch + * to a contexts is via a context execution list, ergo "Execlists". + * + * LRC implementation: + * Regarding the creation of contexts, we have: + * + * - One global default context. + * - One local default context for each opened fd. + * - One local extra context for each context create ioctl call. + * + * Now that ringbuffers belong per-context (and not per-engine, like before) + * and that contexts are uniquely tied to a given engine (and not reusable, + * like before) we need: + * + * - One ringbuffer per-engine inside each context. + * - One backing object per-engine inside each context. + * + * The global default context starts its life with these new objects fully + * allocated and populated. The local default context for each opened fd is + * more complex, because we don't know at creation time which engine is going + * to use them. To handle this, we have implemented a deferred creation of LR + * contexts: + * + * The local context starts its life as a hollow or blank holder, that only + * gets populated for a given engine once we receive an execbuffer. If later + * on we receive another execbuffer ioctl for the same context but a different + * engine, we allocate/populate a new ringbuffer and context backing object and + * so on. + * + * Finally, regarding local contexts created using the ioctl call: as they are + * only allowed with the render ring, we can allocate & populate them right + * away (no need to defer anything, at least for now). + * + * Execlists implementation: + * Execlists are the new method by which, on gen8+ hardware, workloads are + * submitted for execution (as opposed to the legacy, ringbuffer-based, method). + * This method works as follows: + * + * When a request is committed, its commands (the BB start and any leading or + * trailing commands, like the seqno breadcrumbs) are placed in the ringbuffer + * for the appropriate context. The tail pointer in the hardware context is not + * updated at this time, but instead, kept by the driver in the ringbuffer + * structure. A structure representing this request is added to a request queue + * for the appropriate engine: this structure contains a copy of the context's + * tail after the request was written to the ring buffer and a pointer to the + * context itself. + * + * If the engine's request queue was empty before the request was added, the + * queue is processed immediately. Otherwise the queue will be processed during + * a context switch interrupt. In any case, elements on the queue will get sent + * (in pairs) to the GPU's ExecLists Submit Port (ELSP, for short) with a + * globally unique 20-bits submission ID. + * + * When execution of a request completes, the GPU updates the context status + * buffer with a context complete event and generates a context switch interrupt. + * During the interrupt handling, the driver examines the events in the buffer: + * for each context complete event, if the announced ID matches that on the head + * of the request queue, then that request is retired and removed from the queue. + * + * After processing, if any requests were retired and the queue is not empty + * then a new execution list can be submitted. The two requests at the front of + * the queue are next to be submitted but since a context may not occur twice in + * an execution list, if subsequent requests have the same ID as the first then + * the two requests must be combined. This is done simply by discarding requests + * at the head of the queue until either only one requests is left (in which case + * we use a NULL second context) or the first two requests have unique IDs. + * + * By always executing the first two requests in the queue the driver ensures + * that the GPU is kept as busy as possible. In the case where a single context + * completes but a second context is still executing, the request for this second + * context will be at the head of the queue when we remove the first one. This + * request will then be resubmitted along with a new request for a different context, + * which will cause the hardware to continue executing the second request and queue + * the new request (the GPU detects the condition of a context getting preempted + * with the same context and optimizes the context switch flow by not doing + * preemption, but just sampling the new tail pointer). + * + */ + +#include +#include +#include "i915_drv.h" + +#define GEN8_LR_CONTEXT_RENDER_SIZE (20 * PAGE_SIZE) +#define GEN8_LR_CONTEXT_OTHER_SIZE (2 * PAGE_SIZE) + +#define GEN8_LR_CONTEXT_ALIGN 4096 + +#define RING_EXECLIST_QFULL (1 << 0x2) +#define RING_EXECLIST1_VALID (1 << 0x3) +#define RING_EXECLIST0_VALID (1 << 0x4) +#define RING_EXECLIST_ACTIVE_STATUS (3 << 0xE) +#define RING_EXECLIST1_ACTIVE (1 << 0x11) +#define RING_EXECLIST0_ACTIVE (1 << 0x12) + +#define GEN8_CTX_STATUS_IDLE_ACTIVE (1 << 0) +#define GEN8_CTX_STATUS_PREEMPTED (1 << 1) +#define GEN8_CTX_STATUS_ELEMENT_SWITCH (1 << 2) +#define GEN8_CTX_STATUS_ACTIVE_IDLE (1 << 3) +#define GEN8_CTX_STATUS_COMPLETE (1 << 4) +#define GEN8_CTX_STATUS_LITE_RESTORE (1 << 15) + +#define CTX_LRI_HEADER_0 0x01 +#define CTX_CONTEXT_CONTROL 0x02 +#define CTX_RING_HEAD 0x04 +#define CTX_RING_TAIL 0x06 +#define CTX_RING_BUFFER_START 0x08 +#define CTX_RING_BUFFER_CONTROL 0x0a +#define CTX_BB_HEAD_U 0x0c +#define CTX_BB_HEAD_L 0x0e +#define CTX_BB_STATE 0x10 +#define CTX_SECOND_BB_HEAD_U 0x12 +#define CTX_SECOND_BB_HEAD_L 0x14 +#define CTX_SECOND_BB_STATE 0x16 +#define CTX_BB_PER_CTX_PTR 0x18 +#define CTX_RCS_INDIRECT_CTX 0x1a +#define CTX_RCS_INDIRECT_CTX_OFFSET 0x1c +#define CTX_LRI_HEADER_1 0x21 +#define CTX_CTX_TIMESTAMP 0x22 +#define CTX_PDP3_UDW 0x24 +#define CTX_PDP3_LDW 0x26 +#define CTX_PDP2_UDW 0x28 +#define CTX_PDP2_LDW 0x2a +#define CTX_PDP1_UDW 0x2c +#define CTX_PDP1_LDW 0x2e +#define CTX_PDP0_UDW 0x30 +#define CTX_PDP0_LDW 0x32 +#define CTX_LRI_HEADER_2 0x41 +#define CTX_R_PWR_CLK_STATE 0x42 +#define CTX_GPGPU_CSR_BASE_ADDRESS 0x44 + +#define GEN8_CTX_VALID (1<<0) +#define GEN8_CTX_FORCE_PD_RESTORE (1<<1) +#define GEN8_CTX_FORCE_RESTORE (1<<2) +#define GEN8_CTX_L3LLC_COHERENT (1<<5) +#define GEN8_CTX_PRIVILEGE (1<<8) +enum { + ADVANCED_CONTEXT = 0, + LEGACY_CONTEXT, + ADVANCED_AD_CONTEXT, + LEGACY_64B_CONTEXT +}; +#define GEN8_CTX_MODE_SHIFT 3 +enum { + FAULT_AND_HANG = 0, + FAULT_AND_HALT, /* Debug only */ + FAULT_AND_STREAM, + FAULT_AND_CONTINUE /* Unsupported */ +}; +#define GEN8_CTX_ID_SHIFT 32 + +/** + * intel_sanitize_enable_execlists() - sanitize i915.enable_execlists + * @dev: DRM device. + * @enable_execlists: value of i915.enable_execlists module parameter. + * + * Only certain platforms support Execlists (the prerequisites being + * support for Logical Ring Contexts and Aliasing PPGTT or better), + * and only when enabled via module parameter. + * + * Return: 1 if Execlists is supported and has to be enabled. + */ +int intel_sanitize_enable_execlists(struct drm_device *dev, int enable_execlists) +{ + WARN_ON(i915.enable_ppgtt == -1); + + if (enable_execlists == 0) + return 0; + + if (HAS_LOGICAL_RING_CONTEXTS(dev) && USES_PPGTT(dev) && + i915.use_mmio_flip >= 0) + return 1; + + return 0; +} + +/** + * intel_execlists_ctx_id() - get the Execlists Context ID + * @ctx_obj: Logical Ring Context backing object. + * + * Do not confuse with ctx->id! Unfortunately we have a name overload + * here: the old context ID we pass to userspace as a handler so that + * they can refer to a context, and the new context ID we pass to the + * ELSP so that the GPU can inform us of the context status via + * interrupts. + * + * Return: 20-bits globally unique context ID. + */ +u32 intel_execlists_ctx_id(struct drm_i915_gem_object *ctx_obj) +{ + u32 lrca = i915_gem_obj_ggtt_offset(ctx_obj); + + /* LRCA is required to be 4K aligned so the more significant 20 bits + * are globally unique */ + return lrca >> 12; +} + +static uint64_t execlists_ctx_descriptor(struct drm_i915_gem_object *ctx_obj) +{ + uint64_t desc; + uint64_t lrca = i915_gem_obj_ggtt_offset(ctx_obj); + + WARN_ON(lrca & 0xFFFFFFFF00000FFFULL); + + desc = GEN8_CTX_VALID; + desc |= LEGACY_CONTEXT << GEN8_CTX_MODE_SHIFT; + desc |= GEN8_CTX_L3LLC_COHERENT; + desc |= GEN8_CTX_PRIVILEGE; + desc |= lrca; + desc |= (u64)intel_execlists_ctx_id(ctx_obj) << GEN8_CTX_ID_SHIFT; + + /* TODO: WaDisableLiteRestore when we start using semaphore + * signalling between Command Streamers */ + /* desc |= GEN8_CTX_FORCE_RESTORE; */ + + return desc; +} + +static void execlists_elsp_write(struct intel_engine_cs *ring, + struct drm_i915_gem_object *ctx_obj0, + struct drm_i915_gem_object *ctx_obj1) +{ + struct drm_i915_private *dev_priv = ring->dev->dev_private; + uint64_t temp = 0; + uint32_t desc[4]; + unsigned long flags; + + /* XXX: You must always write both descriptors in the order below. */ + if (ctx_obj1) + temp = execlists_ctx_descriptor(ctx_obj1); + else + temp = 0; + desc[1] = (u32)(temp >> 32); + desc[0] = (u32)temp; + + temp = execlists_ctx_descriptor(ctx_obj0); + desc[3] = (u32)(temp >> 32); + desc[2] = (u32)temp; + + /* Set Force Wakeup bit to prevent GT from entering C6 while ELSP writes + * are in progress. + * + * The other problem is that we can't just call gen6_gt_force_wake_get() + * because that function calls intel_runtime_pm_get(), which might sleep. + * Instead, we do the runtime_pm_get/put when creating/destroying requests. + */ + spin_lock_irqsave(&dev_priv->uncore.lock, flags); + if (IS_CHERRYVIEW(dev_priv->dev)) { + if (dev_priv->uncore.fw_rendercount++ == 0) + dev_priv->uncore.funcs.force_wake_get(dev_priv, + FORCEWAKE_RENDER); + if (dev_priv->uncore.fw_mediacount++ == 0) + dev_priv->uncore.funcs.force_wake_get(dev_priv, + FORCEWAKE_MEDIA); + } else { + if (dev_priv->uncore.forcewake_count++ == 0) + dev_priv->uncore.funcs.force_wake_get(dev_priv, + FORCEWAKE_ALL); + } + spin_unlock_irqrestore(&dev_priv->uncore.lock, flags); + + I915_WRITE(RING_ELSP(ring), desc[1]); + I915_WRITE(RING_ELSP(ring), desc[0]); + I915_WRITE(RING_ELSP(ring), desc[3]); + /* The context is automatically loaded after the following */ + I915_WRITE(RING_ELSP(ring), desc[2]); + + /* ELSP is a wo register, so use another nearby reg for posting instead */ + POSTING_READ(RING_EXECLIST_STATUS(ring)); + + /* Release Force Wakeup (see the big comment above). */ + spin_lock_irqsave(&dev_priv->uncore.lock, flags); + if (IS_CHERRYVIEW(dev_priv->dev)) { + if (--dev_priv->uncore.fw_rendercount == 0) + dev_priv->uncore.funcs.force_wake_put(dev_priv, + FORCEWAKE_RENDER); + if (--dev_priv->uncore.fw_mediacount == 0) + dev_priv->uncore.funcs.force_wake_put(dev_priv, + FORCEWAKE_MEDIA); + } else { + if (--dev_priv->uncore.forcewake_count == 0) + dev_priv->uncore.funcs.force_wake_put(dev_priv, + FORCEWAKE_ALL); + } + + spin_unlock_irqrestore(&dev_priv->uncore.lock, flags); +} + +static int execlists_ctx_write_tail(struct drm_i915_gem_object *ctx_obj, u32 tail) +{ + struct page *page; + uint32_t *reg_state; + + page = i915_gem_object_get_page(ctx_obj, 1); + reg_state = kmap_atomic(page); + + reg_state[CTX_RING_TAIL+1] = tail; + + kunmap_atomic(reg_state); + + return 0; +} + +static int execlists_submit_context(struct intel_engine_cs *ring, + struct intel_context *to0, u32 tail0, + struct intel_context *to1, u32 tail1) +{ + struct drm_i915_gem_object *ctx_obj0; + struct drm_i915_gem_object *ctx_obj1 = NULL; + + ctx_obj0 = to0->engine[ring->id].state; + BUG_ON(!ctx_obj0); + WARN_ON(!i915_gem_obj_is_pinned(ctx_obj0)); + + execlists_ctx_write_tail(ctx_obj0, tail0); + + if (to1) { + ctx_obj1 = to1->engine[ring->id].state; + BUG_ON(!ctx_obj1); + WARN_ON(!i915_gem_obj_is_pinned(ctx_obj1)); + + execlists_ctx_write_tail(ctx_obj1, tail1); + } + + execlists_elsp_write(ring, ctx_obj0, ctx_obj1); + + return 0; +} + +static void execlists_context_unqueue(struct intel_engine_cs *ring) +{ + struct intel_ctx_submit_request *req0 = NULL, *req1 = NULL; + struct intel_ctx_submit_request *cursor = NULL, *tmp = NULL; + struct drm_i915_private *dev_priv = ring->dev->dev_private; + + assert_spin_locked(&ring->execlist_lock); + + if (list_empty(&ring->execlist_queue)) + return; + + /* Try to read in pairs */ + list_for_each_entry_safe(cursor, tmp, &ring->execlist_queue, + execlist_link) { + if (!req0) { + req0 = cursor; + } else if (req0->ctx == cursor->ctx) { + /* Same ctx: ignore first request, as second request + * will update tail past first request's workload */ + cursor->elsp_submitted = req0->elsp_submitted; + list_del(&req0->execlist_link); + queue_work(dev_priv->wq, &req0->work); + req0 = cursor; + } else { + req1 = cursor; + break; + } + } + + WARN_ON(req1 && req1->elsp_submitted); + + WARN_ON(execlists_submit_context(ring, req0->ctx, req0->tail, + req1 ? req1->ctx : NULL, + req1 ? req1->tail : 0)); + + req0->elsp_submitted++; + if (req1) + req1->elsp_submitted++; +} + +static bool execlists_check_remove_request(struct intel_engine_cs *ring, + u32 request_id) +{ + struct drm_i915_private *dev_priv = ring->dev->dev_private; + struct intel_ctx_submit_request *head_req; + + assert_spin_locked(&ring->execlist_lock); + + head_req = list_first_entry_or_null(&ring->execlist_queue, + struct intel_ctx_submit_request, + execlist_link); + + if (head_req != NULL) { + struct drm_i915_gem_object *ctx_obj = + head_req->ctx->engine[ring->id].state; + if (intel_execlists_ctx_id(ctx_obj) == request_id) { + WARN(head_req->elsp_submitted == 0, + "Never submitted head request\n"); + + if (--head_req->elsp_submitted <= 0) { + list_del(&head_req->execlist_link); + queue_work(dev_priv->wq, &head_req->work); + return true; + } + } + } + + return false; +} + +/** + * intel_execlists_handle_ctx_events() - handle Context Switch interrupts + * @ring: Engine Command Streamer to handle. + * + * Check the unread Context Status Buffers and manage the submission of new + * contexts to the ELSP accordingly. + */ +void intel_execlists_handle_ctx_events(struct intel_engine_cs *ring) +{ + struct drm_i915_private *dev_priv = ring->dev->dev_private; + u32 status_pointer; + u8 read_pointer; + u8 write_pointer; + u32 status; + u32 status_id; + u32 submit_contexts = 0; + + status_pointer = I915_READ(RING_CONTEXT_STATUS_PTR(ring)); + + read_pointer = ring->next_context_status_buffer; + write_pointer = status_pointer & 0x07; + if (read_pointer > write_pointer) + write_pointer += 6; + + spin_lock(&ring->execlist_lock); + + while (read_pointer < write_pointer) { + read_pointer++; + status = I915_READ(RING_CONTEXT_STATUS_BUF(ring) + + (read_pointer % 6) * 8); + status_id = I915_READ(RING_CONTEXT_STATUS_BUF(ring) + + (read_pointer % 6) * 8 + 4); + + if (status & GEN8_CTX_STATUS_PREEMPTED) { + if (status & GEN8_CTX_STATUS_LITE_RESTORE) { + if (execlists_check_remove_request(ring, status_id)) + WARN(1, "Lite Restored request removed from queue\n"); + } else + WARN(1, "Preemption without Lite Restore\n"); + } + + if ((status & GEN8_CTX_STATUS_ACTIVE_IDLE) || + (status & GEN8_CTX_STATUS_ELEMENT_SWITCH)) { + if (execlists_check_remove_request(ring, status_id)) + submit_contexts++; + } + } + + if (submit_contexts != 0) + execlists_context_unqueue(ring); + + spin_unlock(&ring->execlist_lock); + + WARN(submit_contexts > 2, "More than two context complete events?\n"); + ring->next_context_status_buffer = write_pointer % 6; + + I915_WRITE(RING_CONTEXT_STATUS_PTR(ring), + ((u32)ring->next_context_status_buffer & 0x07) << 8); +} + +static void execlists_free_request_task(struct work_struct *work) +{ + struct intel_ctx_submit_request *req = + container_of(work, struct intel_ctx_submit_request, work); + struct drm_device *dev = req->ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + intel_runtime_pm_put(dev_priv); + + mutex_lock(&dev->struct_mutex); + i915_gem_context_unreference(req->ctx); + mutex_unlock(&dev->struct_mutex); + + kfree(req); +} + +static int execlists_context_queue(struct intel_engine_cs *ring, + struct intel_context *to, + u32 tail) +{ + struct intel_ctx_submit_request *req = NULL, *cursor; + struct drm_i915_private *dev_priv = ring->dev->dev_private; + unsigned long flags; + int num_elements = 0; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (req == NULL) + return -ENOMEM; + req->ctx = to; + i915_gem_context_reference(req->ctx); + req->ring = ring; + req->tail = tail; + INIT_WORK(&req->work, execlists_free_request_task); + + intel_runtime_pm_get(dev_priv); + + spin_lock_irqsave(&ring->execlist_lock, flags); + + list_for_each_entry(cursor, &ring->execlist_queue, execlist_link) + if (++num_elements > 2) + break; + + if (num_elements > 2) { + struct intel_ctx_submit_request *tail_req; + + tail_req = list_last_entry(&ring->execlist_queue, + struct intel_ctx_submit_request, + execlist_link); + + if (to == tail_req->ctx) { + WARN(tail_req->elsp_submitted != 0, + "More than 2 already-submitted reqs queued\n"); + list_del(&tail_req->execlist_link); + queue_work(dev_priv->wq, &tail_req->work); + } + } + + list_add_tail(&req->execlist_link, &ring->execlist_queue); + if (num_elements == 0) + execlists_context_unqueue(ring); + + spin_unlock_irqrestore(&ring->execlist_lock, flags); + + return 0; +} + +static int logical_ring_invalidate_all_caches(struct intel_ringbuffer *ringbuf) +{ + struct intel_engine_cs *ring = ringbuf->ring; + uint32_t flush_domains; + int ret; + + flush_domains = 0; + if (ring->gpu_caches_dirty) + flush_domains = I915_GEM_GPU_DOMAINS; + + ret = ring->emit_flush(ringbuf, I915_GEM_GPU_DOMAINS, flush_domains); + if (ret) + return ret; + + ring->gpu_caches_dirty = false; + return 0; +} + +static int execlists_move_to_gpu(struct intel_ringbuffer *ringbuf, + struct list_head *vmas) +{ + struct intel_engine_cs *ring = ringbuf->ring; + struct i915_vma *vma; + uint32_t flush_domains = 0; + bool flush_chipset = false; + int ret; + + list_for_each_entry(vma, vmas, exec_list) { + struct drm_i915_gem_object *obj = vma->obj; + + ret = i915_gem_object_sync(obj, ring); + if (ret) + return ret; + + if (obj->base.write_domain & I915_GEM_DOMAIN_CPU) + flush_chipset |= i915_gem_clflush_object(obj, false); + + flush_domains |= obj->base.write_domain; + } + + if (flush_domains & I915_GEM_DOMAIN_GTT) + wmb(); + + /* Unconditionally invalidate gpu caches and ensure that we do flush + * any residual writes from the previous batch. + */ + return logical_ring_invalidate_all_caches(ringbuf); +} + +/** + * execlists_submission() - submit a batchbuffer for execution, Execlists style + * @dev: DRM device. + * @file: DRM file. + * @ring: Engine Command Streamer to submit to. + * @ctx: Context to employ for this submission. + * @args: execbuffer call arguments. + * @vmas: list of vmas. + * @batch_obj: the batchbuffer to submit. + * @exec_start: batchbuffer start virtual address pointer. + * @flags: translated execbuffer call flags. + * + * This is the evil twin version of i915_gem_ringbuffer_submission. It abstracts + * away the submission details of the execbuffer ioctl call. + * + * Return: non-zero if the submission fails. + */ +int intel_execlists_submission(struct drm_device *dev, struct drm_file *file, + struct intel_engine_cs *ring, + struct intel_context *ctx, + struct drm_i915_gem_execbuffer2 *args, + struct list_head *vmas, + struct drm_i915_gem_object *batch_obj, + u64 exec_start, u32 flags) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_ringbuffer *ringbuf = ctx->engine[ring->id].ringbuf; + int instp_mode; + u32 instp_mask; + int ret; + + instp_mode = args->flags & I915_EXEC_CONSTANTS_MASK; + instp_mask = I915_EXEC_CONSTANTS_MASK; + switch (instp_mode) { + case I915_EXEC_CONSTANTS_REL_GENERAL: + case I915_EXEC_CONSTANTS_ABSOLUTE: + case I915_EXEC_CONSTANTS_REL_SURFACE: + if (instp_mode != 0 && ring != &dev_priv->ring[RCS]) { + DRM_DEBUG("non-0 rel constants mode on non-RCS\n"); + return -EINVAL; + } + + if (instp_mode != dev_priv->relative_constants_mode) { + if (instp_mode == I915_EXEC_CONSTANTS_REL_SURFACE) { + DRM_DEBUG("rel surface constants mode invalid on gen5+\n"); + return -EINVAL; + } + + /* The HW changed the meaning on this bit on gen6 */ + instp_mask &= ~I915_EXEC_CONSTANTS_REL_SURFACE; + } + break; + default: + DRM_DEBUG("execbuf with unknown constants: %d\n", instp_mode); + return -EINVAL; + } + + if (args->num_cliprects != 0) { + DRM_DEBUG("clip rectangles are only valid on pre-gen5\n"); + return -EINVAL; + } else { + if (args->DR4 == 0xffffffff) { + DRM_DEBUG("UXA submitting garbage DR4, fixing up\n"); + args->DR4 = 0; + } + + if (args->DR1 || args->DR4 || args->cliprects_ptr) { + DRM_DEBUG("0 cliprects but dirt in cliprects fields\n"); + return -EINVAL; + } + } + + if (args->flags & I915_EXEC_GEN7_SOL_RESET) { + DRM_DEBUG("sol reset is gen7 only\n"); + return -EINVAL; + } + + ret = execlists_move_to_gpu(ringbuf, vmas); + if (ret) + return ret; + + if (ring == &dev_priv->ring[RCS] && + instp_mode != dev_priv->relative_constants_mode) { + ret = intel_logical_ring_begin(ringbuf, 4); + if (ret) + return ret; + + intel_logical_ring_emit(ringbuf, MI_NOOP); + intel_logical_ring_emit(ringbuf, MI_LOAD_REGISTER_IMM(1)); + intel_logical_ring_emit(ringbuf, INSTPM); + intel_logical_ring_emit(ringbuf, instp_mask << 16 | instp_mode); + intel_logical_ring_advance(ringbuf); + + dev_priv->relative_constants_mode = instp_mode; + } + + ret = ring->emit_bb_start(ringbuf, exec_start, flags); + if (ret) + return ret; + + i915_gem_execbuffer_move_to_active(vmas, ring); + i915_gem_execbuffer_retire_commands(dev, file, ring, batch_obj); + + return 0; +} + +void intel_logical_ring_stop(struct intel_engine_cs *ring) +{ + struct drm_i915_private *dev_priv = ring->dev->dev_private; + int ret; + + if (!intel_ring_initialized(ring)) + return; + + ret = intel_ring_idle(ring); + if (ret && !i915_reset_in_progress(&to_i915(ring->dev)->gpu_error)) + DRM_ERROR("failed to quiesce %s whilst cleaning up: %d\n", + ring->name, ret); + + /* TODO: Is this correct with Execlists enabled? */ + I915_WRITE_MODE(ring, _MASKED_BIT_ENABLE(STOP_RING)); + if (wait_for_atomic((I915_READ_MODE(ring) & MODE_IDLE) != 0, 1000)) { + DRM_ERROR("%s :timed out trying to stop ring\n", ring->name); + return; + } + I915_WRITE_MODE(ring, _MASKED_BIT_DISABLE(STOP_RING)); +} + +int logical_ring_flush_all_caches(struct intel_ringbuffer *ringbuf) +{ + struct intel_engine_cs *ring = ringbuf->ring; + int ret; + + if (!ring->gpu_caches_dirty) + return 0; + + ret = ring->emit_flush(ringbuf, 0, I915_GEM_GPU_DOMAINS); + if (ret) + return ret; + + ring->gpu_caches_dirty = false; + return 0; +} + +/** + * intel_logical_ring_advance_and_submit() - advance the tail and submit the workload + * @ringbuf: Logical Ringbuffer to advance. + * + * The tail is updated in our logical ringbuffer struct, not in the actual context. What + * really happens during submission is that the context and current tail will be placed + * on a queue waiting for the ELSP to be ready to accept a new context submission. At that + * point, the tail *inside* the context is updated and the ELSP written to. + */ +void intel_logical_ring_advance_and_submit(struct intel_ringbuffer *ringbuf) +{ + struct intel_engine_cs *ring = ringbuf->ring; + struct intel_context *ctx = ringbuf->FIXME_lrc_ctx; + + intel_logical_ring_advance(ringbuf); + + if (intel_ring_stopped(ring)) + return; + + execlists_context_queue(ring, ctx, ringbuf->tail); +} + +static int logical_ring_alloc_seqno(struct intel_engine_cs *ring, + struct intel_context *ctx) +{ + if (ring->outstanding_lazy_seqno) + return 0; + + if (ring->preallocated_lazy_request == NULL) { + struct drm_i915_gem_request *request; + + request = kmalloc(sizeof(*request), GFP_KERNEL); + if (request == NULL) + return -ENOMEM; + + /* Hold a reference to the context this request belongs to + * (we will need it when the time comes to emit/retire the + * request). + */ + request->ctx = ctx; + i915_gem_context_reference(request->ctx); + + ring->preallocated_lazy_request = request; + } + + return i915_gem_get_seqno(ring->dev, &ring->outstanding_lazy_seqno); +} + +static int logical_ring_wait_request(struct intel_ringbuffer *ringbuf, + int bytes) +{ + struct intel_engine_cs *ring = ringbuf->ring; + struct drm_i915_gem_request *request; + u32 seqno = 0; + int ret; + + if (ringbuf->last_retired_head != -1) { + ringbuf->head = ringbuf->last_retired_head; + ringbuf->last_retired_head = -1; + + ringbuf->space = intel_ring_space(ringbuf); + if (ringbuf->space >= bytes) + return 0; + } + + list_for_each_entry(request, &ring->request_list, list) { + if (__intel_ring_space(request->tail, ringbuf->tail, + ringbuf->size) >= bytes) { + seqno = request->seqno; + break; + } + } + + if (seqno == 0) + return -ENOSPC; + + ret = i915_wait_seqno(ring, seqno); + if (ret) + return ret; + + i915_gem_retire_requests_ring(ring); + ringbuf->head = ringbuf->last_retired_head; + ringbuf->last_retired_head = -1; + + ringbuf->space = intel_ring_space(ringbuf); + return 0; +} + +static int logical_ring_wait_for_space(struct intel_ringbuffer *ringbuf, + int bytes) +{ + struct intel_engine_cs *ring = ringbuf->ring; + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long end; + int ret; + + ret = logical_ring_wait_request(ringbuf, bytes); + if (ret != -ENOSPC) + return ret; + + /* Force the context submission in case we have been skipping it */ + intel_logical_ring_advance_and_submit(ringbuf); + + /* With GEM the hangcheck timer should kick us out of the loop, + * leaving it early runs the risk of corrupting GEM state (due + * to running on almost untested codepaths). But on resume + * timers don't work yet, so prevent a complete hang in that + * case by choosing an insanely large timeout. */ + end = jiffies + 60 * HZ; + + do { + ringbuf->head = I915_READ_HEAD(ring); + ringbuf->space = intel_ring_space(ringbuf); + if (ringbuf->space >= bytes) { + ret = 0; + break; + } + + msleep(1); + + if (dev_priv->mm.interruptible && signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + ret = i915_gem_check_wedge(&dev_priv->gpu_error, + dev_priv->mm.interruptible); + if (ret) + break; + + if (time_after(jiffies, end)) { + ret = -EBUSY; + break; + } + } while (1); + + return ret; +} + +static int logical_ring_wrap_buffer(struct intel_ringbuffer *ringbuf) +{ + uint32_t __iomem *virt; + int rem = ringbuf->size - ringbuf->tail; + + if (ringbuf->space < rem) { + int ret = logical_ring_wait_for_space(ringbuf, rem); + + if (ret) + return ret; + } + + virt = ringbuf->virtual_start + ringbuf->tail; + rem /= 4; + while (rem--) + iowrite32(MI_NOOP, virt++); + + ringbuf->tail = 0; + ringbuf->space = intel_ring_space(ringbuf); + + return 0; +} + +static int logical_ring_prepare(struct intel_ringbuffer *ringbuf, int bytes) +{ + int ret; + + if (unlikely(ringbuf->tail + bytes > ringbuf->effective_size)) { + ret = logical_ring_wrap_buffer(ringbuf); + if (unlikely(ret)) + return ret; + } + + if (unlikely(ringbuf->space < bytes)) { + ret = logical_ring_wait_for_space(ringbuf, bytes); + if (unlikely(ret)) + return ret; + } + + return 0; +} + +/** + * intel_logical_ring_begin() - prepare the logical ringbuffer to accept some commands + * + * @ringbuf: Logical ringbuffer. + * @num_dwords: number of DWORDs that we plan to write to the ringbuffer. + * + * The ringbuffer might not be ready to accept the commands right away (maybe it needs to + * be wrapped, or wait a bit for the tail to be updated). This function takes care of that + * and also preallocates a request (every workload submission is still mediated through + * requests, same as it did with legacy ringbuffer submission). + * + * Return: non-zero if the ringbuffer is not ready to be written to. + */ +int intel_logical_ring_begin(struct intel_ringbuffer *ringbuf, int num_dwords) +{ + struct intel_engine_cs *ring = ringbuf->ring; + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int ret; + + ret = i915_gem_check_wedge(&dev_priv->gpu_error, + dev_priv->mm.interruptible); + if (ret) + return ret; + + ret = logical_ring_prepare(ringbuf, num_dwords * sizeof(uint32_t)); + if (ret) + return ret; + + /* Preallocate the olr before touching the ring */ + ret = logical_ring_alloc_seqno(ring, ringbuf->FIXME_lrc_ctx); + if (ret) + return ret; + + ringbuf->space -= num_dwords * sizeof(uint32_t); + return 0; +} + +static int gen8_init_common_ring(struct intel_engine_cs *ring) +{ + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + I915_WRITE_IMR(ring, ~(ring->irq_enable_mask | ring->irq_keep_mask)); + I915_WRITE(RING_HWSTAM(ring->mmio_base), 0xffffffff); + + I915_WRITE(RING_MODE_GEN7(ring), + _MASKED_BIT_DISABLE(GFX_REPLAY_MODE) | + _MASKED_BIT_ENABLE(GFX_RUN_LIST_ENABLE)); + POSTING_READ(RING_MODE_GEN7(ring)); + DRM_DEBUG_DRIVER("Execlists enabled for %s\n", ring->name); + + memset(&ring->hangcheck, 0, sizeof(ring->hangcheck)); + + return 0; +} + +static int gen8_init_render_ring(struct intel_engine_cs *ring) +{ + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int ret; + + ret = gen8_init_common_ring(ring); + if (ret) + return ret; + + /* We need to disable the AsyncFlip performance optimisations in order + * to use MI_WAIT_FOR_EVENT within the CS. It should already be + * programmed to '1' on all products. + * + * WaDisableAsyncFlipPerfMode:snb,ivb,hsw,vlv,bdw,chv + */ + I915_WRITE(MI_MODE, _MASKED_BIT_ENABLE(ASYNC_FLIP_PERF_DISABLE)); + + ret = intel_init_pipe_control(ring); + if (ret) + return ret; + + I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_FORCE_ORDERING)); + + return ret; +} + +static int gen8_emit_bb_start(struct intel_ringbuffer *ringbuf, + u64 offset, unsigned flags) +{ + bool ppgtt = !(flags & I915_DISPATCH_SECURE); + int ret; + + ret = intel_logical_ring_begin(ringbuf, 4); + if (ret) + return ret; + + /* FIXME(BDW): Address space and security selectors. */ + intel_logical_ring_emit(ringbuf, MI_BATCH_BUFFER_START_GEN8 | (ppgtt<<8)); + intel_logical_ring_emit(ringbuf, lower_32_bits(offset)); + intel_logical_ring_emit(ringbuf, upper_32_bits(offset)); + intel_logical_ring_emit(ringbuf, MI_NOOP); + intel_logical_ring_advance(ringbuf); + + return 0; +} + +static bool gen8_logical_ring_get_irq(struct intel_engine_cs *ring) +{ + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long flags; + + if (!dev->irq_enabled) + return false; + + spin_lock_irqsave(&dev_priv->irq_lock, flags); + if (ring->irq_refcount++ == 0) { + I915_WRITE_IMR(ring, ~(ring->irq_enable_mask | ring->irq_keep_mask)); + POSTING_READ(RING_IMR(ring->mmio_base)); + } + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); + + return true; +} + +static void gen8_logical_ring_put_irq(struct intel_engine_cs *ring) +{ + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long flags; + + spin_lock_irqsave(&dev_priv->irq_lock, flags); + if (--ring->irq_refcount == 0) { + I915_WRITE_IMR(ring, ~ring->irq_keep_mask); + POSTING_READ(RING_IMR(ring->mmio_base)); + } + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); +} + +static int gen8_emit_flush(struct intel_ringbuffer *ringbuf, + u32 invalidate_domains, + u32 unused) +{ + struct intel_engine_cs *ring = ringbuf->ring; + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t cmd; + int ret; + + ret = intel_logical_ring_begin(ringbuf, 4); + if (ret) + return ret; + + cmd = MI_FLUSH_DW + 1; + + if (ring == &dev_priv->ring[VCS]) { + if (invalidate_domains & I915_GEM_GPU_DOMAINS) + cmd |= MI_INVALIDATE_TLB | MI_INVALIDATE_BSD | + MI_FLUSH_DW_STORE_INDEX | + MI_FLUSH_DW_OP_STOREDW; + } else { + if (invalidate_domains & I915_GEM_DOMAIN_RENDER) + cmd |= MI_INVALIDATE_TLB | MI_FLUSH_DW_STORE_INDEX | + MI_FLUSH_DW_OP_STOREDW; + } + + intel_logical_ring_emit(ringbuf, cmd); + intel_logical_ring_emit(ringbuf, + I915_GEM_HWS_SCRATCH_ADDR | + MI_FLUSH_DW_USE_GTT); + intel_logical_ring_emit(ringbuf, 0); /* upper addr */ + intel_logical_ring_emit(ringbuf, 0); /* value */ + intel_logical_ring_advance(ringbuf); + + return 0; +} + +static int gen8_emit_flush_render(struct intel_ringbuffer *ringbuf, + u32 invalidate_domains, + u32 flush_domains) +{ + struct intel_engine_cs *ring = ringbuf->ring; + u32 scratch_addr = ring->scratch.gtt_offset + 2 * CACHELINE_BYTES; + u32 flags = 0; + int ret; + + flags |= PIPE_CONTROL_CS_STALL; + + if (flush_domains) { + flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; + flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; + } + + if (invalidate_domains) { + flags |= PIPE_CONTROL_TLB_INVALIDATE; + flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_QW_WRITE; + flags |= PIPE_CONTROL_GLOBAL_GTT_IVB; + } + + ret = intel_logical_ring_begin(ringbuf, 6); + if (ret) + return ret; + + intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6)); + intel_logical_ring_emit(ringbuf, flags); + intel_logical_ring_emit(ringbuf, scratch_addr); + intel_logical_ring_emit(ringbuf, 0); + intel_logical_ring_emit(ringbuf, 0); + intel_logical_ring_emit(ringbuf, 0); + intel_logical_ring_advance(ringbuf); + + return 0; +} + +static u32 gen8_get_seqno(struct intel_engine_cs *ring, bool lazy_coherency) +{ + return intel_read_status_page(ring, I915_GEM_HWS_INDEX); +} + +static void gen8_set_seqno(struct intel_engine_cs *ring, u32 seqno) +{ + intel_write_status_page(ring, I915_GEM_HWS_INDEX, seqno); +} + +static int gen8_emit_request(struct intel_ringbuffer *ringbuf) +{ + struct intel_engine_cs *ring = ringbuf->ring; + u32 cmd; + int ret; + + ret = intel_logical_ring_begin(ringbuf, 6); + if (ret) + return ret; + + cmd = MI_STORE_DWORD_IMM_GEN8; + cmd |= MI_GLOBAL_GTT; + + intel_logical_ring_emit(ringbuf, cmd); + intel_logical_ring_emit(ringbuf, + (ring->status_page.gfx_addr + + (I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT))); + intel_logical_ring_emit(ringbuf, 0); + intel_logical_ring_emit(ringbuf, ring->outstanding_lazy_seqno); + intel_logical_ring_emit(ringbuf, MI_USER_INTERRUPT); + intel_logical_ring_emit(ringbuf, MI_NOOP); + intel_logical_ring_advance_and_submit(ringbuf); + + return 0; +} + +/** + * intel_logical_ring_cleanup() - deallocate the Engine Command Streamer + * + * @ring: Engine Command Streamer. + * + */ +void intel_logical_ring_cleanup(struct intel_engine_cs *ring) +{ + struct drm_i915_private *dev_priv = ring->dev->dev_private; + + if (!intel_ring_initialized(ring)) + return; + + intel_logical_ring_stop(ring); + WARN_ON((I915_READ_MODE(ring) & MODE_IDLE) == 0); + ring->preallocated_lazy_request = NULL; + ring->outstanding_lazy_seqno = 0; + + if (ring->cleanup) + ring->cleanup(ring); + + i915_cmd_parser_fini_ring(ring); + + if (ring->status_page.obj) { + kunmap(sg_page(ring->status_page.obj->pages->sgl)); + ring->status_page.obj = NULL; + } +} + +static int logical_ring_init(struct drm_device *dev, struct intel_engine_cs *ring) +{ + int ret; + + /* Intentionally left blank. */ + ring->buffer = NULL; + + ring->dev = dev; + INIT_LIST_HEAD(&ring->active_list); + INIT_LIST_HEAD(&ring->request_list); + init_waitqueue_head(&ring->irq_queue); + + INIT_LIST_HEAD(&ring->execlist_queue); + spin_lock_init(&ring->execlist_lock); + ring->next_context_status_buffer = 0; + + ret = i915_cmd_parser_init_ring(ring); + if (ret) + return ret; + + if (ring->init) { + ret = ring->init(ring); + if (ret) + return ret; + } + + ret = intel_lr_context_deferred_create(ring->default_context, ring); + + return ret; +} + +static int logical_render_ring_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_engine_cs *ring = &dev_priv->ring[RCS]; + + ring->name = "render ring"; + ring->id = RCS; + ring->mmio_base = RENDER_RING_BASE; + ring->irq_enable_mask = + GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT; + ring->irq_keep_mask = + GT_CONTEXT_SWITCH_INTERRUPT << GEN8_RCS_IRQ_SHIFT; + if (HAS_L3_DPF(dev)) + ring->irq_keep_mask |= GT_RENDER_L3_PARITY_ERROR_INTERRUPT; + + ring->init = gen8_init_render_ring; + ring->cleanup = intel_fini_pipe_control; + ring->get_seqno = gen8_get_seqno; + ring->set_seqno = gen8_set_seqno; + ring->emit_request = gen8_emit_request; + ring->emit_flush = gen8_emit_flush_render; + ring->irq_get = gen8_logical_ring_get_irq; + ring->irq_put = gen8_logical_ring_put_irq; + ring->emit_bb_start = gen8_emit_bb_start; + + return logical_ring_init(dev, ring); +} + +static int logical_bsd_ring_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_engine_cs *ring = &dev_priv->ring[VCS]; + + ring->name = "bsd ring"; + ring->id = VCS; + ring->mmio_base = GEN6_BSD_RING_BASE; + ring->irq_enable_mask = + GT_RENDER_USER_INTERRUPT << GEN8_VCS1_IRQ_SHIFT; + ring->irq_keep_mask = + GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VCS1_IRQ_SHIFT; + + ring->init = gen8_init_common_ring; + ring->get_seqno = gen8_get_seqno; + ring->set_seqno = gen8_set_seqno; + ring->emit_request = gen8_emit_request; + ring->emit_flush = gen8_emit_flush; + ring->irq_get = gen8_logical_ring_get_irq; + ring->irq_put = gen8_logical_ring_put_irq; + ring->emit_bb_start = gen8_emit_bb_start; + + return logical_ring_init(dev, ring); +} + +static int logical_bsd2_ring_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_engine_cs *ring = &dev_priv->ring[VCS2]; + + ring->name = "bds2 ring"; + ring->id = VCS2; + ring->mmio_base = GEN8_BSD2_RING_BASE; + ring->irq_enable_mask = + GT_RENDER_USER_INTERRUPT << GEN8_VCS2_IRQ_SHIFT; + ring->irq_keep_mask = + GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VCS2_IRQ_SHIFT; + + ring->init = gen8_init_common_ring; + ring->get_seqno = gen8_get_seqno; + ring->set_seqno = gen8_set_seqno; + ring->emit_request = gen8_emit_request; + ring->emit_flush = gen8_emit_flush; + ring->irq_get = gen8_logical_ring_get_irq; + ring->irq_put = gen8_logical_ring_put_irq; + ring->emit_bb_start = gen8_emit_bb_start; + + return logical_ring_init(dev, ring); +} + +static int logical_blt_ring_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_engine_cs *ring = &dev_priv->ring[BCS]; + + ring->name = "blitter ring"; + ring->id = BCS; + ring->mmio_base = BLT_RING_BASE; + ring->irq_enable_mask = + GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT; + ring->irq_keep_mask = + GT_CONTEXT_SWITCH_INTERRUPT << GEN8_BCS_IRQ_SHIFT; + + ring->init = gen8_init_common_ring; + ring->get_seqno = gen8_get_seqno; + ring->set_seqno = gen8_set_seqno; + ring->emit_request = gen8_emit_request; + ring->emit_flush = gen8_emit_flush; + ring->irq_get = gen8_logical_ring_get_irq; + ring->irq_put = gen8_logical_ring_put_irq; + ring->emit_bb_start = gen8_emit_bb_start; + + return logical_ring_init(dev, ring); +} + +static int logical_vebox_ring_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_engine_cs *ring = &dev_priv->ring[VECS]; + + ring->name = "video enhancement ring"; + ring->id = VECS; + ring->mmio_base = VEBOX_RING_BASE; + ring->irq_enable_mask = + GT_RENDER_USER_INTERRUPT << GEN8_VECS_IRQ_SHIFT; + ring->irq_keep_mask = + GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VECS_IRQ_SHIFT; + + ring->init = gen8_init_common_ring; + ring->get_seqno = gen8_get_seqno; + ring->set_seqno = gen8_set_seqno; + ring->emit_request = gen8_emit_request; + ring->emit_flush = gen8_emit_flush; + ring->irq_get = gen8_logical_ring_get_irq; + ring->irq_put = gen8_logical_ring_put_irq; + ring->emit_bb_start = gen8_emit_bb_start; + + return logical_ring_init(dev, ring); +} + +/** + * intel_logical_rings_init() - allocate, populate and init the Engine Command Streamers + * @dev: DRM device. + * + * This function inits the engines for an Execlists submission style (the equivalent in the + * legacy ringbuffer submission world would be i915_gem_init_rings). It does it only for + * those engines that are present in the hardware. + * + * Return: non-zero if the initialization failed. + */ +int intel_logical_rings_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int ret; + + ret = logical_render_ring_init(dev); + if (ret) + return ret; + + if (HAS_BSD(dev)) { + ret = logical_bsd_ring_init(dev); + if (ret) + goto cleanup_render_ring; + } + + if (HAS_BLT(dev)) { + ret = logical_blt_ring_init(dev); + if (ret) + goto cleanup_bsd_ring; + } + + if (HAS_VEBOX(dev)) { + ret = logical_vebox_ring_init(dev); + if (ret) + goto cleanup_blt_ring; + } + + if (HAS_BSD2(dev)) { + ret = logical_bsd2_ring_init(dev); + if (ret) + goto cleanup_vebox_ring; + } + + ret = i915_gem_set_seqno(dev, ((u32)~0 - 0x1000)); + if (ret) + goto cleanup_bsd2_ring; + + return 0; + +cleanup_bsd2_ring: + intel_logical_ring_cleanup(&dev_priv->ring[VCS2]); +cleanup_vebox_ring: + intel_logical_ring_cleanup(&dev_priv->ring[VECS]); +cleanup_blt_ring: + intel_logical_ring_cleanup(&dev_priv->ring[BCS]); +cleanup_bsd_ring: + intel_logical_ring_cleanup(&dev_priv->ring[VCS]); +cleanup_render_ring: + intel_logical_ring_cleanup(&dev_priv->ring[RCS]); + + return ret; +} + +int intel_lr_context_render_state_init(struct intel_engine_cs *ring, + struct intel_context *ctx) +{ + struct intel_ringbuffer *ringbuf = ctx->engine[ring->id].ringbuf; + struct render_state so; + struct drm_i915_file_private *file_priv = ctx->file_priv; + struct drm_file *file = file_priv ? file_priv->file : NULL; + int ret; + + ret = i915_gem_render_state_prepare(ring, &so); + if (ret) + return ret; + + if (so.rodata == NULL) + return 0; + + ret = ring->emit_bb_start(ringbuf, + so.ggtt_offset, + I915_DISPATCH_SECURE); + if (ret) + goto out; + + i915_vma_move_to_active(i915_gem_obj_to_ggtt(so.obj), ring); + + ret = __i915_add_request(ring, file, so.obj, NULL); + /* intel_logical_ring_add_request moves object to inactive if it + * fails */ +out: + i915_gem_render_state_fini(&so); + return ret; +} + +static int +populate_lr_context(struct intel_context *ctx, struct drm_i915_gem_object *ctx_obj, + struct intel_engine_cs *ring, struct intel_ringbuffer *ringbuf) +{ + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *ring_obj = ringbuf->obj; + struct i915_hw_ppgtt *ppgtt = ctx->ppgtt; + struct page *page; + uint32_t *reg_state; + int ret; + + if (!ppgtt) + ppgtt = dev_priv->mm.aliasing_ppgtt; + + ret = i915_gem_object_set_to_cpu_domain(ctx_obj, true); + if (ret) { + DRM_DEBUG_DRIVER("Could not set to CPU domain\n"); + return ret; + } + + ret = i915_gem_object_get_pages(ctx_obj); + if (ret) { + DRM_DEBUG_DRIVER("Could not get object pages\n"); + return ret; + } + + i915_gem_object_pin_pages(ctx_obj); + + /* The second page of the context object contains some fields which must + * be set up prior to the first execution. */ + page = i915_gem_object_get_page(ctx_obj, 1); + reg_state = kmap_atomic(page); + + /* A context is actually a big batch buffer with several MI_LOAD_REGISTER_IMM + * commands followed by (reg, value) pairs. The values we are setting here are + * only for the first context restore: on a subsequent save, the GPU will + * recreate this batchbuffer with new values (including all the missing + * MI_LOAD_REGISTER_IMM commands that we are not initializing here). */ + if (ring->id == RCS) + reg_state[CTX_LRI_HEADER_0] = MI_LOAD_REGISTER_IMM(14); + else + reg_state[CTX_LRI_HEADER_0] = MI_LOAD_REGISTER_IMM(11); + reg_state[CTX_LRI_HEADER_0] |= MI_LRI_FORCE_POSTED; + reg_state[CTX_CONTEXT_CONTROL] = RING_CONTEXT_CONTROL(ring); + reg_state[CTX_CONTEXT_CONTROL+1] = + _MASKED_BIT_ENABLE((1<<3) | MI_RESTORE_INHIBIT); + reg_state[CTX_RING_HEAD] = RING_HEAD(ring->mmio_base); + reg_state[CTX_RING_HEAD+1] = 0; + reg_state[CTX_RING_TAIL] = RING_TAIL(ring->mmio_base); + reg_state[CTX_RING_TAIL+1] = 0; + reg_state[CTX_RING_BUFFER_START] = RING_START(ring->mmio_base); + reg_state[CTX_RING_BUFFER_START+1] = i915_gem_obj_ggtt_offset(ring_obj); + reg_state[CTX_RING_BUFFER_CONTROL] = RING_CTL(ring->mmio_base); + reg_state[CTX_RING_BUFFER_CONTROL+1] = + ((ringbuf->size - PAGE_SIZE) & RING_NR_PAGES) | RING_VALID; + reg_state[CTX_BB_HEAD_U] = ring->mmio_base + 0x168; + reg_state[CTX_BB_HEAD_U+1] = 0; + reg_state[CTX_BB_HEAD_L] = ring->mmio_base + 0x140; + reg_state[CTX_BB_HEAD_L+1] = 0; + reg_state[CTX_BB_STATE] = ring->mmio_base + 0x110; + reg_state[CTX_BB_STATE+1] = (1<<5); + reg_state[CTX_SECOND_BB_HEAD_U] = ring->mmio_base + 0x11c; + reg_state[CTX_SECOND_BB_HEAD_U+1] = 0; + reg_state[CTX_SECOND_BB_HEAD_L] = ring->mmio_base + 0x114; + reg_state[CTX_SECOND_BB_HEAD_L+1] = 0; + reg_state[CTX_SECOND_BB_STATE] = ring->mmio_base + 0x118; + reg_state[CTX_SECOND_BB_STATE+1] = 0; + if (ring->id == RCS) { + /* TODO: according to BSpec, the register state context + * for CHV does not have these. OTOH, these registers do + * exist in CHV. I'm waiting for a clarification */ + reg_state[CTX_BB_PER_CTX_PTR] = ring->mmio_base + 0x1c0; + reg_state[CTX_BB_PER_CTX_PTR+1] = 0; + reg_state[CTX_RCS_INDIRECT_CTX] = ring->mmio_base + 0x1c4; + reg_state[CTX_RCS_INDIRECT_CTX+1] = 0; + reg_state[CTX_RCS_INDIRECT_CTX_OFFSET] = ring->mmio_base + 0x1c8; + reg_state[CTX_RCS_INDIRECT_CTX_OFFSET+1] = 0; + } + reg_state[CTX_LRI_HEADER_1] = MI_LOAD_REGISTER_IMM(9); + reg_state[CTX_LRI_HEADER_1] |= MI_LRI_FORCE_POSTED; + reg_state[CTX_CTX_TIMESTAMP] = ring->mmio_base + 0x3a8; + reg_state[CTX_CTX_TIMESTAMP+1] = 0; + reg_state[CTX_PDP3_UDW] = GEN8_RING_PDP_UDW(ring, 3); + reg_state[CTX_PDP3_LDW] = GEN8_RING_PDP_LDW(ring, 3); + reg_state[CTX_PDP2_UDW] = GEN8_RING_PDP_UDW(ring, 2); + reg_state[CTX_PDP2_LDW] = GEN8_RING_PDP_LDW(ring, 2); + reg_state[CTX_PDP1_UDW] = GEN8_RING_PDP_UDW(ring, 1); + reg_state[CTX_PDP1_LDW] = GEN8_RING_PDP_LDW(ring, 1); + reg_state[CTX_PDP0_UDW] = GEN8_RING_PDP_UDW(ring, 0); + reg_state[CTX_PDP0_LDW] = GEN8_RING_PDP_LDW(ring, 0); + reg_state[CTX_PDP3_UDW+1] = upper_32_bits(ppgtt->pd_dma_addr[3]); + reg_state[CTX_PDP3_LDW+1] = lower_32_bits(ppgtt->pd_dma_addr[3]); + reg_state[CTX_PDP2_UDW+1] = upper_32_bits(ppgtt->pd_dma_addr[2]); + reg_state[CTX_PDP2_LDW+1] = lower_32_bits(ppgtt->pd_dma_addr[2]); + reg_state[CTX_PDP1_UDW+1] = upper_32_bits(ppgtt->pd_dma_addr[1]); + reg_state[CTX_PDP1_LDW+1] = lower_32_bits(ppgtt->pd_dma_addr[1]); + reg_state[CTX_PDP0_UDW+1] = upper_32_bits(ppgtt->pd_dma_addr[0]); + reg_state[CTX_PDP0_LDW+1] = lower_32_bits(ppgtt->pd_dma_addr[0]); + if (ring->id == RCS) { + reg_state[CTX_LRI_HEADER_2] = MI_LOAD_REGISTER_IMM(1); + reg_state[CTX_R_PWR_CLK_STATE] = 0x20c8; + reg_state[CTX_R_PWR_CLK_STATE+1] = 0; + } + + kunmap_atomic(reg_state); + + ctx_obj->dirty = 1; + set_page_dirty(page); + i915_gem_object_unpin_pages(ctx_obj); + + return 0; +} + +/** + * intel_lr_context_free() - free the LRC specific bits of a context + * @ctx: the LR context to free. + * + * The real context freeing is done in i915_gem_context_free: this only + * takes care of the bits that are LRC related: the per-engine backing + * objects and the logical ringbuffer. + */ +void intel_lr_context_free(struct intel_context *ctx) +{ + int i; + + for (i = 0; i < I915_NUM_RINGS; i++) { + struct drm_i915_gem_object *ctx_obj = ctx->engine[i].state; + struct intel_ringbuffer *ringbuf = ctx->engine[i].ringbuf; + + if (ctx_obj) { + intel_destroy_ringbuffer_obj(ringbuf); + kfree(ringbuf); + i915_gem_object_ggtt_unpin(ctx_obj); + drm_gem_object_unreference(&ctx_obj->base); + } + } +} + +static uint32_t get_lr_context_size(struct intel_engine_cs *ring) +{ + int ret = 0; + + WARN_ON(INTEL_INFO(ring->dev)->gen != 8); + + switch (ring->id) { + case RCS: + ret = GEN8_LR_CONTEXT_RENDER_SIZE; + break; + case VCS: + case BCS: + case VECS: + case VCS2: + ret = GEN8_LR_CONTEXT_OTHER_SIZE; + break; + } + + return ret; +} + +/** + * intel_lr_context_deferred_create() - create the LRC specific bits of a context + * @ctx: LR context to create. + * @ring: engine to be used with the context. + * + * This function can be called more than once, with different engines, if we plan + * to use the context with them. The context backing objects and the ringbuffers + * (specially the ringbuffer backing objects) suck a lot of memory up, and that's why + * the creation is a deferred call: it's better to make sure first that we need to use + * a given ring with the context. + * + * Return: non-zero on eror. + */ +int intel_lr_context_deferred_create(struct intel_context *ctx, + struct intel_engine_cs *ring) +{ + struct drm_device *dev = ring->dev; + struct drm_i915_gem_object *ctx_obj; + uint32_t context_size; + struct intel_ringbuffer *ringbuf; + int ret; + + WARN_ON(ctx->legacy_hw_ctx.rcs_state != NULL); + if (ctx->engine[ring->id].state) + return 0; + + context_size = round_up(get_lr_context_size(ring), 4096); + + ctx_obj = i915_gem_alloc_context_obj(dev, context_size); + if (IS_ERR(ctx_obj)) { + ret = PTR_ERR(ctx_obj); + DRM_DEBUG_DRIVER("Alloc LRC backing obj failed: %d\n", ret); + return ret; + } + + ret = i915_gem_obj_ggtt_pin(ctx_obj, GEN8_LR_CONTEXT_ALIGN, 0); + if (ret) { + DRM_DEBUG_DRIVER("Pin LRC backing obj failed: %d\n", ret); + drm_gem_object_unreference(&ctx_obj->base); + return ret; + } + + ringbuf = kzalloc(sizeof(*ringbuf), GFP_KERNEL); + if (!ringbuf) { + DRM_DEBUG_DRIVER("Failed to allocate ringbuffer %s\n", + ring->name); + i915_gem_object_ggtt_unpin(ctx_obj); + drm_gem_object_unreference(&ctx_obj->base); + ret = -ENOMEM; + return ret; + } + + ringbuf->ring = ring; + ringbuf->FIXME_lrc_ctx = ctx; + + ringbuf->size = 32 * PAGE_SIZE; + ringbuf->effective_size = ringbuf->size; + ringbuf->head = 0; + ringbuf->tail = 0; + ringbuf->space = ringbuf->size; + ringbuf->last_retired_head = -1; + + /* TODO: For now we put this in the mappable region so that we can reuse + * the existing ringbuffer code which ioremaps it. When we start + * creating many contexts, this will no longer work and we must switch + * to a kmapish interface. + */ + ret = intel_alloc_ringbuffer_obj(dev, ringbuf); + if (ret) { + DRM_DEBUG_DRIVER("Failed to allocate ringbuffer obj %s: %d\n", + ring->name, ret); + goto error; + } + + ret = populate_lr_context(ctx, ctx_obj, ring, ringbuf); + if (ret) { + DRM_DEBUG_DRIVER("Failed to populate LRC: %d\n", ret); + intel_destroy_ringbuffer_obj(ringbuf); + goto error; + } + + ctx->engine[ring->id].ringbuf = ringbuf; + ctx->engine[ring->id].state = ctx_obj; + + if (ctx == ring->default_context) { + /* The status page is offset 0 from the default context object + * in LRC mode. */ + ring->status_page.gfx_addr = i915_gem_obj_ggtt_offset(ctx_obj); + ring->status_page.page_addr = + kmap(sg_page(ctx_obj->pages->sgl)); + if (ring->status_page.page_addr == NULL) + return -ENOMEM; + ring->status_page.obj = ctx_obj; + } + + if (ring->id == RCS && !ctx->rcs_initialized) { + ret = intel_lr_context_render_state_init(ring, ctx); + if (ret) { + DRM_ERROR("Init render state failed: %d\n", ret); + ctx->engine[ring->id].ringbuf = NULL; + ctx->engine[ring->id].state = NULL; + intel_destroy_ringbuffer_obj(ringbuf); + goto error; + } + ctx->rcs_initialized = true; + } + + return 0; + +error: + kfree(ringbuf); + i915_gem_object_ggtt_unpin(ctx_obj); + drm_gem_object_unreference(&ctx_obj->base); + return ret; +} diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h new file mode 100644 index 0000000000000000000000000000000000000000..33c3b4bf28c56322c77235575d12fd83532f947b --- /dev/null +++ b/drivers/gpu/drm/i915/intel_lrc.h @@ -0,0 +1,114 @@ +/* + * Copyright © 2014 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef _INTEL_LRC_H_ +#define _INTEL_LRC_H_ + +/* Execlists regs */ +#define RING_ELSP(ring) ((ring)->mmio_base+0x230) +#define RING_EXECLIST_STATUS(ring) ((ring)->mmio_base+0x234) +#define RING_CONTEXT_CONTROL(ring) ((ring)->mmio_base+0x244) +#define RING_CONTEXT_STATUS_BUF(ring) ((ring)->mmio_base+0x370) +#define RING_CONTEXT_STATUS_PTR(ring) ((ring)->mmio_base+0x3a0) + +/* Logical Rings */ +void intel_logical_ring_stop(struct intel_engine_cs *ring); +void intel_logical_ring_cleanup(struct intel_engine_cs *ring); +int intel_logical_rings_init(struct drm_device *dev); + +int logical_ring_flush_all_caches(struct intel_ringbuffer *ringbuf); +void intel_logical_ring_advance_and_submit(struct intel_ringbuffer *ringbuf); +/** + * intel_logical_ring_advance() - advance the ringbuffer tail + * @ringbuf: Ringbuffer to advance. + * + * The tail is only updated in our logical ringbuffer struct. + */ +static inline void intel_logical_ring_advance(struct intel_ringbuffer *ringbuf) +{ + ringbuf->tail &= ringbuf->size - 1; +} +/** + * intel_logical_ring_emit() - write a DWORD to the ringbuffer. + * @ringbuf: Ringbuffer to write to. + * @data: DWORD to write. + */ +static inline void intel_logical_ring_emit(struct intel_ringbuffer *ringbuf, + u32 data) +{ + iowrite32(data, ringbuf->virtual_start + ringbuf->tail); + ringbuf->tail += 4; +} +int intel_logical_ring_begin(struct intel_ringbuffer *ringbuf, int num_dwords); + +/* Logical Ring Contexts */ +int intel_lr_context_render_state_init(struct intel_engine_cs *ring, + struct intel_context *ctx); +void intel_lr_context_free(struct intel_context *ctx); +int intel_lr_context_deferred_create(struct intel_context *ctx, + struct intel_engine_cs *ring); + +/* Execlists */ +int intel_sanitize_enable_execlists(struct drm_device *dev, int enable_execlists); +int intel_execlists_submission(struct drm_device *dev, struct drm_file *file, + struct intel_engine_cs *ring, + struct intel_context *ctx, + struct drm_i915_gem_execbuffer2 *args, + struct list_head *vmas, + struct drm_i915_gem_object *batch_obj, + u64 exec_start, u32 flags); +u32 intel_execlists_ctx_id(struct drm_i915_gem_object *ctx_obj); + +/** + * struct intel_ctx_submit_request - queued context submission request + * @ctx: Context to submit to the ELSP. + * @ring: Engine to submit it to. + * @tail: how far in the context's ringbuffer this request goes to. + * @execlist_link: link in the submission queue. + * @work: workqueue for processing this request in a bottom half. + * @elsp_submitted: no. of times this request has been sent to the ELSP. + * + * The ELSP only accepts two elements at a time, so we queue context/tail + * pairs on a given queue (ring->execlist_queue) until the hardware is + * available. The queue serves a double purpose: we also use it to keep track + * of the up to 2 contexts currently in the hardware (usually one in execution + * and the other queued up by the GPU): We only remove elements from the head + * of the queue when the hardware informs us that an element has been + * completed. + * + * All accesses to the queue are mediated by a spinlock (ring->execlist_lock). + */ +struct intel_ctx_submit_request { + struct intel_context *ctx; + struct intel_engine_cs *ring; + u32 tail; + + struct list_head execlist_link; + struct work_struct work; + + int elsp_submitted; +}; + +void intel_execlists_handle_ctx_events(struct intel_engine_cs *ring); + +#endif /* _INTEL_LRC_H_ */ diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index fdf40267249c1031a4a52a8f110b41fbf93ff61f..a6bd1422e38fd33fccc19f2fd78684291c0f54d3 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -823,8 +823,7 @@ bool intel_is_dual_link_lvds(struct drm_device *dev) struct intel_encoder *encoder; struct intel_lvds_encoder *lvds_encoder; - list_for_each_entry(encoder, &dev->mode_config.encoder_list, - base.head) { + for_each_intel_encoder(dev, encoder) { if (encoder->type == INTEL_OUTPUT_LVDS) { lvds_encoder = to_lvds_encoder(&encoder->base); diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 8e374449c6b562356f47ac2ba169f974f8e7226f..18784470a760def6c6fd4af4938e53145acd4cab 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -751,6 +751,8 @@ void intel_panel_disable_backlight(struct intel_connector *connector) spin_lock_irqsave(&dev_priv->backlight_lock, flags); + if (panel->backlight.device) + panel->backlight.device->props.power = FB_BLANK_POWERDOWN; panel->backlight.enabled = false; dev_priv->display.disable_backlight(connector); @@ -957,6 +959,8 @@ void intel_panel_enable_backlight(struct intel_connector *connector) dev_priv->display.enable_backlight(connector); panel->backlight.enabled = true; + if (panel->backlight.device) + panel->backlight.device->props.power = FB_BLANK_UNBLANK; spin_unlock_irqrestore(&dev_priv->backlight_lock, flags); } @@ -965,6 +969,7 @@ void intel_panel_enable_backlight(struct intel_connector *connector) static int intel_backlight_device_update_status(struct backlight_device *bd) { struct intel_connector *connector = bl_get_data(bd); + struct intel_panel *panel = &connector->panel; struct drm_device *dev = connector->base.dev; drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); @@ -972,6 +977,23 @@ static int intel_backlight_device_update_status(struct backlight_device *bd) bd->props.brightness, bd->props.max_brightness); intel_panel_set_backlight(connector, bd->props.brightness, bd->props.max_brightness); + + /* + * Allow flipping bl_power as a sub-state of enabled. Sadly the + * backlight class device does not make it easy to to differentiate + * between callbacks for brightness and bl_power, so our backlight_power + * callback needs to take this into account. + */ + if (panel->backlight.enabled) { + if (panel->backlight_power) { + bool enable = bd->props.power == FB_BLANK_UNBLANK && + bd->props.brightness != 0; + panel->backlight_power(connector, enable); + } + } else { + bd->props.power = FB_BLANK_POWERDOWN; + } + drm_modeset_unlock(&dev->mode_config.connection_mutex); return 0; } @@ -1023,6 +1045,11 @@ static int intel_backlight_device_register(struct intel_connector *connector) panel->backlight.level, props.max_brightness); + if (panel->backlight.enabled) + props.power = FB_BLANK_UNBLANK; + else + props.power = FB_BLANK_POWERDOWN; + /* * Note: using the same name independent of the connector prevents * registration of multiple backlight devices in the driver. @@ -1203,7 +1230,7 @@ static int vlv_setup_backlight(struct intel_connector *connector) enum pipe pipe; u32 ctl, ctl2, val; - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { u32 cur_val = I915_READ(VLV_BLC_PWM_CTL(pipe)); /* Skip if the modulation freq is already set */ diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 40c12295c0bde4648d319f3da48205ab131fc92a..c27b6140bfd10e3912006f994da2bb75090c6b82 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -309,6 +309,9 @@ static void gen7_enable_fbc(struct drm_crtc *crtc) dpfc_ctl |= IVB_DPFC_CTL_FENCE_EN; + if (dev_priv->fbc.false_color) + dpfc_ctl |= FBC_CTL_FALSE_COLOR; + I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN); if (IS_IVYBRIDGE(dev)) { @@ -342,6 +345,16 @@ bool intel_fbc_enabled(struct drm_device *dev) return dev_priv->display.fbc_enabled(dev); } +void gen8_fbc_sw_flush(struct drm_device *dev, u32 value) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (!IS_GEN8(dev)) + return; + + I915_WRITE(MSG_FBC_REND_STATE, value); +} + static void intel_fbc_work_fn(struct work_struct *__work) { struct intel_fbc_work *work = @@ -578,6 +591,12 @@ void intel_update_fbc(struct drm_device *dev) DRM_DEBUG_KMS("framebuffer not tiled or fenced, disabling compression\n"); goto out_disable; } + if (INTEL_INFO(dev)->gen <= 4 && !IS_G4X(dev) && + to_intel_plane(crtc->primary)->rotation != BIT(DRM_ROTATE_0)) { + if (set_no_fbc_reason(dev_priv, FBC_UNSUPPORTED_MODE)) + DRM_DEBUG_KMS("Rotation unsupported, disabling\n"); + goto out_disable; + } /* If the kernel debugger is active, always disable compression */ if (in_dbg_master()) @@ -853,7 +872,7 @@ void intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable) * A value of 5us seems to be a good balance; safe for very low end * platforms but not overly aggressive on lower latency configs. */ -static const int latency_ns = 5000; +static const int pessimal_latency_ns = 5000; static int i9xx_get_fifo_size(struct drm_device *dev, int plane) { @@ -982,13 +1001,20 @@ static const struct intel_watermark_params i915_wm_info = { .guard_size = 2, .cacheline_size = I915_FIFO_LINE_SIZE, }; -static const struct intel_watermark_params i830_wm_info = { +static const struct intel_watermark_params i830_a_wm_info = { .fifo_size = I855GM_FIFO_SIZE, .max_wm = I915_MAX_WM, .default_wm = 1, .guard_size = 2, .cacheline_size = I830_FIFO_LINE_SIZE, }; +static const struct intel_watermark_params i830_bc_wm_info = { + .fifo_size = I855GM_FIFO_SIZE, + .max_wm = I915_MAX_WM/2, + .default_wm = 1, + .guard_size = 2, + .cacheline_size = I830_FIFO_LINE_SIZE, +}; static const struct intel_watermark_params i845_wm_info = { .fifo_size = I830_FIFO_SIZE, .max_wm = I915_MAX_WM, @@ -1044,6 +1070,17 @@ static unsigned long intel_calculate_wm(unsigned long clock_in_khz, wm_size = wm->max_wm; if (wm_size <= 0) wm_size = wm->default_wm; + + /* + * Bspec seems to indicate that the value shouldn't be lower than + * 'burst size + 1'. Certainly 830 is quite unhappy with low values. + * Lets go for 8 which is the burst size since certain platforms + * already use a hardcoded 8 (which is what the spec says should be + * done). + */ + if (wm_size <= 8) + wm_size = 8; + return wm_size; } @@ -1268,33 +1305,27 @@ static bool g4x_compute_srwm(struct drm_device *dev, display, cursor); } -static bool vlv_compute_drain_latency(struct drm_device *dev, - int plane, - int *plane_prec_mult, - int *plane_dl, - int *cursor_prec_mult, - int *cursor_dl) +static bool vlv_compute_drain_latency(struct drm_crtc *crtc, + int pixel_size, + int *prec_mult, + int *drain_latency) { - struct drm_crtc *crtc; - int clock, pixel_size; int entries; + int clock = to_intel_crtc(crtc)->config.adjusted_mode.crtc_clock; - crtc = intel_get_crtc_for_plane(dev, plane); - if (!intel_crtc_active(crtc)) + if (WARN(clock == 0, "Pixel clock is zero!\n")) return false; - clock = to_intel_crtc(crtc)->config.adjusted_mode.crtc_clock; - pixel_size = crtc->primary->fb->bits_per_pixel / 8; /* BPP */ + if (WARN(pixel_size == 0, "Pixel size is zero!\n")) + return false; - entries = (clock / 1000) * pixel_size; - *plane_prec_mult = (entries > 128) ? - DRAIN_LATENCY_PRECISION_64 : DRAIN_LATENCY_PRECISION_32; - *plane_dl = (64 * (*plane_prec_mult) * 4) / entries; + entries = DIV_ROUND_UP(clock, 1000) * pixel_size; + *prec_mult = (entries > 128) ? DRAIN_LATENCY_PRECISION_64 : + DRAIN_LATENCY_PRECISION_32; + *drain_latency = (64 * (*prec_mult) * 4) / entries; - entries = (clock / 1000) * 4; /* BPP is always 4 for cursor */ - *cursor_prec_mult = (entries > 128) ? - DRAIN_LATENCY_PRECISION_64 : DRAIN_LATENCY_PRECISION_32; - *cursor_dl = (64 * (*cursor_prec_mult) * 4) / entries; + if (*drain_latency > DRAIN_LATENCY_MASK) + *drain_latency = DRAIN_LATENCY_MASK; return true; } @@ -1307,39 +1338,48 @@ static bool vlv_compute_drain_latency(struct drm_device *dev, * latency value. */ -static void vlv_update_drain_latency(struct drm_device *dev) +static void vlv_update_drain_latency(struct drm_crtc *crtc) { - struct drm_i915_private *dev_priv = dev->dev_private; - int planea_prec, planea_dl, planeb_prec, planeb_dl; - int cursora_prec, cursora_dl, cursorb_prec, cursorb_dl; - int plane_prec_mult, cursor_prec_mult; /* Precision multiplier is - either 16 or 32 */ + struct drm_i915_private *dev_priv = crtc->dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pixel_size; + int drain_latency; + enum pipe pipe = intel_crtc->pipe; + int plane_prec, prec_mult, plane_dl; + + plane_dl = I915_READ(VLV_DDL(pipe)) & ~(DDL_PLANE_PRECISION_64 | + DRAIN_LATENCY_MASK | DDL_CURSOR_PRECISION_64 | + (DRAIN_LATENCY_MASK << DDL_CURSOR_SHIFT)); - /* For plane A, Cursor A */ - if (vlv_compute_drain_latency(dev, 0, &plane_prec_mult, &planea_dl, - &cursor_prec_mult, &cursora_dl)) { - cursora_prec = (cursor_prec_mult == DRAIN_LATENCY_PRECISION_32) ? - DDL_CURSORA_PRECISION_32 : DDL_CURSORA_PRECISION_64; - planea_prec = (plane_prec_mult == DRAIN_LATENCY_PRECISION_32) ? - DDL_PLANEA_PRECISION_32 : DDL_PLANEA_PRECISION_64; + if (!intel_crtc_active(crtc)) { + I915_WRITE(VLV_DDL(pipe), plane_dl); + return; + } - I915_WRITE(VLV_DDL1, cursora_prec | - (cursora_dl << DDL_CURSORA_SHIFT) | - planea_prec | planea_dl); + /* Primary plane Drain Latency */ + pixel_size = crtc->primary->fb->bits_per_pixel / 8; /* BPP */ + if (vlv_compute_drain_latency(crtc, pixel_size, &prec_mult, &drain_latency)) { + plane_prec = (prec_mult == DRAIN_LATENCY_PRECISION_64) ? + DDL_PLANE_PRECISION_64 : + DDL_PLANE_PRECISION_32; + plane_dl |= plane_prec | drain_latency; } - /* For plane B, Cursor B */ - if (vlv_compute_drain_latency(dev, 1, &plane_prec_mult, &planeb_dl, - &cursor_prec_mult, &cursorb_dl)) { - cursorb_prec = (cursor_prec_mult == DRAIN_LATENCY_PRECISION_32) ? - DDL_CURSORB_PRECISION_32 : DDL_CURSORB_PRECISION_64; - planeb_prec = (plane_prec_mult == DRAIN_LATENCY_PRECISION_32) ? - DDL_PLANEB_PRECISION_32 : DDL_PLANEB_PRECISION_64; + /* Cursor Drain Latency + * BPP is always 4 for cursor + */ + pixel_size = 4; - I915_WRITE(VLV_DDL2, cursorb_prec | - (cursorb_dl << DDL_CURSORB_SHIFT) | - planeb_prec | planeb_dl); + /* Program cursor DL only if it is enabled */ + if (intel_crtc->cursor_base && + vlv_compute_drain_latency(crtc, pixel_size, &prec_mult, &drain_latency)) { + plane_prec = (prec_mult == DRAIN_LATENCY_PRECISION_64) ? + DDL_CURSOR_PRECISION_64 : + DDL_CURSOR_PRECISION_32; + plane_dl |= plane_prec | (drain_latency << DDL_CURSOR_SHIFT); } + + I915_WRITE(VLV_DDL(pipe), plane_dl); } #define single_plane_enabled(mask) is_power_of_2(mask) @@ -1355,20 +1395,92 @@ static void valleyview_update_wm(struct drm_crtc *crtc) unsigned int enabled = 0; bool cxsr_enabled; - vlv_update_drain_latency(dev); + vlv_update_drain_latency(crtc); + + if (g4x_compute_wm0(dev, PIPE_A, + &valleyview_wm_info, pessimal_latency_ns, + &valleyview_cursor_wm_info, pessimal_latency_ns, + &planea_wm, &cursora_wm)) + enabled |= 1 << PIPE_A; + + if (g4x_compute_wm0(dev, PIPE_B, + &valleyview_wm_info, pessimal_latency_ns, + &valleyview_cursor_wm_info, pessimal_latency_ns, + &planeb_wm, &cursorb_wm)) + enabled |= 1 << PIPE_B; + + if (single_plane_enabled(enabled) && + g4x_compute_srwm(dev, ffs(enabled) - 1, + sr_latency_ns, + &valleyview_wm_info, + &valleyview_cursor_wm_info, + &plane_sr, &ignore_cursor_sr) && + g4x_compute_srwm(dev, ffs(enabled) - 1, + 2*sr_latency_ns, + &valleyview_wm_info, + &valleyview_cursor_wm_info, + &ignore_plane_sr, &cursor_sr)) { + cxsr_enabled = true; + } else { + cxsr_enabled = false; + intel_set_memory_cxsr(dev_priv, false); + plane_sr = cursor_sr = 0; + } + + DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, " + "B: plane=%d, cursor=%d, SR: plane=%d, cursor=%d\n", + planea_wm, cursora_wm, + planeb_wm, cursorb_wm, + plane_sr, cursor_sr); + + I915_WRITE(DSPFW1, + (plane_sr << DSPFW_SR_SHIFT) | + (cursorb_wm << DSPFW_CURSORB_SHIFT) | + (planeb_wm << DSPFW_PLANEB_SHIFT) | + (planea_wm << DSPFW_PLANEA_SHIFT)); + I915_WRITE(DSPFW2, + (I915_READ(DSPFW2) & ~DSPFW_CURSORA_MASK) | + (cursora_wm << DSPFW_CURSORA_SHIFT)); + I915_WRITE(DSPFW3, + (I915_READ(DSPFW3) & ~DSPFW_CURSOR_SR_MASK) | + (cursor_sr << DSPFW_CURSOR_SR_SHIFT)); + + if (cxsr_enabled) + intel_set_memory_cxsr(dev_priv, true); +} + +static void cherryview_update_wm(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + static const int sr_latency_ns = 12000; + struct drm_i915_private *dev_priv = dev->dev_private; + int planea_wm, planeb_wm, planec_wm; + int cursora_wm, cursorb_wm, cursorc_wm; + int plane_sr, cursor_sr; + int ignore_plane_sr, ignore_cursor_sr; + unsigned int enabled = 0; + bool cxsr_enabled; + + vlv_update_drain_latency(crtc); if (g4x_compute_wm0(dev, PIPE_A, - &valleyview_wm_info, latency_ns, - &valleyview_cursor_wm_info, latency_ns, + &valleyview_wm_info, pessimal_latency_ns, + &valleyview_cursor_wm_info, pessimal_latency_ns, &planea_wm, &cursora_wm)) enabled |= 1 << PIPE_A; if (g4x_compute_wm0(dev, PIPE_B, - &valleyview_wm_info, latency_ns, - &valleyview_cursor_wm_info, latency_ns, + &valleyview_wm_info, pessimal_latency_ns, + &valleyview_cursor_wm_info, pessimal_latency_ns, &planeb_wm, &cursorb_wm)) enabled |= 1 << PIPE_B; + if (g4x_compute_wm0(dev, PIPE_C, + &valleyview_wm_info, pessimal_latency_ns, + &valleyview_cursor_wm_info, pessimal_latency_ns, + &planec_wm, &cursorc_wm)) + enabled |= 1 << PIPE_C; + if (single_plane_enabled(enabled) && g4x_compute_srwm(dev, ffs(enabled) - 1, sr_latency_ns, @@ -1387,27 +1499,66 @@ static void valleyview_update_wm(struct drm_crtc *crtc) plane_sr = cursor_sr = 0; } - DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, B: plane=%d, cursor=%d, SR: plane=%d, cursor=%d\n", + DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, " + "B: plane=%d, cursor=%d, C: plane=%d, cursor=%d, " + "SR: plane=%d, cursor=%d\n", planea_wm, cursora_wm, planeb_wm, cursorb_wm, + planec_wm, cursorc_wm, plane_sr, cursor_sr); I915_WRITE(DSPFW1, (plane_sr << DSPFW_SR_SHIFT) | (cursorb_wm << DSPFW_CURSORB_SHIFT) | (planeb_wm << DSPFW_PLANEB_SHIFT) | - planea_wm); + (planea_wm << DSPFW_PLANEA_SHIFT)); I915_WRITE(DSPFW2, (I915_READ(DSPFW2) & ~DSPFW_CURSORA_MASK) | (cursora_wm << DSPFW_CURSORA_SHIFT)); I915_WRITE(DSPFW3, (I915_READ(DSPFW3) & ~DSPFW_CURSOR_SR_MASK) | (cursor_sr << DSPFW_CURSOR_SR_SHIFT)); + I915_WRITE(DSPFW9_CHV, + (I915_READ(DSPFW9_CHV) & ~(DSPFW_PLANEC_MASK | + DSPFW_CURSORC_MASK)) | + (planec_wm << DSPFW_PLANEC_SHIFT) | + (cursorc_wm << DSPFW_CURSORC_SHIFT)); if (cxsr_enabled) intel_set_memory_cxsr(dev_priv, true); } +static void valleyview_update_sprite_wm(struct drm_plane *plane, + struct drm_crtc *crtc, + uint32_t sprite_width, + uint32_t sprite_height, + int pixel_size, + bool enabled, bool scaled) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = to_intel_plane(plane)->pipe; + int sprite = to_intel_plane(plane)->plane; + int drain_latency; + int plane_prec; + int sprite_dl; + int prec_mult; + + sprite_dl = I915_READ(VLV_DDL(pipe)) & ~(DDL_SPRITE_PRECISION_64(sprite) | + (DRAIN_LATENCY_MASK << DDL_SPRITE_SHIFT(sprite))); + + if (enabled && vlv_compute_drain_latency(crtc, pixel_size, &prec_mult, + &drain_latency)) { + plane_prec = (prec_mult == DRAIN_LATENCY_PRECISION_64) ? + DDL_SPRITE_PRECISION_64(sprite) : + DDL_SPRITE_PRECISION_32(sprite); + sprite_dl |= plane_prec | + (drain_latency << DDL_SPRITE_SHIFT(sprite)); + } + + I915_WRITE(VLV_DDL(pipe), sprite_dl); +} + static void g4x_update_wm(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -1419,14 +1570,14 @@ static void g4x_update_wm(struct drm_crtc *crtc) bool cxsr_enabled; if (g4x_compute_wm0(dev, PIPE_A, - &g4x_wm_info, latency_ns, - &g4x_cursor_wm_info, latency_ns, + &g4x_wm_info, pessimal_latency_ns, + &g4x_cursor_wm_info, pessimal_latency_ns, &planea_wm, &cursora_wm)) enabled |= 1 << PIPE_A; if (g4x_compute_wm0(dev, PIPE_B, - &g4x_wm_info, latency_ns, - &g4x_cursor_wm_info, latency_ns, + &g4x_wm_info, pessimal_latency_ns, + &g4x_cursor_wm_info, pessimal_latency_ns, &planeb_wm, &cursorb_wm)) enabled |= 1 << PIPE_B; @@ -1443,7 +1594,8 @@ static void g4x_update_wm(struct drm_crtc *crtc) plane_sr = cursor_sr = 0; } - DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, B: plane=%d, cursor=%d, SR: plane=%d, cursor=%d\n", + DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, " + "B: plane=%d, cursor=%d, SR: plane=%d, cursor=%d\n", planea_wm, cursora_wm, planeb_wm, cursorb_wm, plane_sr, cursor_sr); @@ -1452,7 +1604,7 @@ static void g4x_update_wm(struct drm_crtc *crtc) (plane_sr << DSPFW_SR_SHIFT) | (cursorb_wm << DSPFW_CURSORB_SHIFT) | (planeb_wm << DSPFW_PLANEB_SHIFT) | - planea_wm); + (planea_wm << DSPFW_PLANEA_SHIFT)); I915_WRITE(DSPFW2, (I915_READ(DSPFW2) & ~DSPFW_CURSORA_MASK) | (cursora_wm << DSPFW_CURSORA_SHIFT)); @@ -1526,8 +1678,11 @@ static void i965_update_wm(struct drm_crtc *unused_crtc) /* 965 has limitations... */ I915_WRITE(DSPFW1, (srwm << DSPFW_SR_SHIFT) | - (8 << 16) | (8 << 8) | (8 << 0)); - I915_WRITE(DSPFW2, (8 << 8) | (8 << 0)); + (8 << DSPFW_CURSORB_SHIFT) | + (8 << DSPFW_PLANEB_SHIFT) | + (8 << DSPFW_PLANEA_SHIFT)); + I915_WRITE(DSPFW2, (8 << DSPFW_CURSORA_SHIFT) | + (8 << DSPFW_PLANEC_SHIFT_OLD)); /* update cursor SR watermark */ I915_WRITE(DSPFW3, (cursor_sr << DSPFW_CURSOR_SR_SHIFT)); @@ -1552,7 +1707,7 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc) else if (!IS_GEN2(dev)) wm_info = &i915_wm_info; else - wm_info = &i830_wm_info; + wm_info = &i830_a_wm_info; fifo_size = dev_priv->display.get_fifo_size(dev, 0); crtc = intel_get_crtc_for_plane(dev, 0); @@ -1565,10 +1720,16 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc) adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode; planea_wm = intel_calculate_wm(adjusted_mode->crtc_clock, wm_info, fifo_size, cpp, - latency_ns); + pessimal_latency_ns); enabled = crtc; - } else + } else { planea_wm = fifo_size - wm_info->guard_size; + if (planea_wm > (long)wm_info->max_wm) + planea_wm = wm_info->max_wm; + } + + if (IS_GEN2(dev)) + wm_info = &i830_bc_wm_info; fifo_size = dev_priv->display.get_fifo_size(dev, 1); crtc = intel_get_crtc_for_plane(dev, 1); @@ -1581,13 +1742,16 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc) adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode; planeb_wm = intel_calculate_wm(adjusted_mode->crtc_clock, wm_info, fifo_size, cpp, - latency_ns); + pessimal_latency_ns); if (enabled == NULL) enabled = crtc; else enabled = NULL; - } else + } else { planeb_wm = fifo_size - wm_info->guard_size; + if (planeb_wm > (long)wm_info->max_wm) + planeb_wm = wm_info->max_wm; + } DRM_DEBUG_KMS("FIFO watermarks - A: %d, B: %d\n", planea_wm, planeb_wm); @@ -1674,7 +1838,7 @@ static void i845_update_wm(struct drm_crtc *unused_crtc) planea_wm = intel_calculate_wm(adjusted_mode->crtc_clock, &i845_wm_info, dev_priv->display.get_fifo_size(dev, 0), - 4, latency_ns); + 4, pessimal_latency_ns); fwater_lo = I915_READ(FW_BLC) & ~0xfff; fwater_lo |= (3<<8) | planea_wm; @@ -2527,7 +2691,7 @@ static struct intel_pipe_wm *ilk_find_best_result(struct drm_device *dev, #define WM_DIRTY_FBC (1 << 24) #define WM_DIRTY_DDB (1 << 25) -static unsigned int ilk_compute_wm_dirty(struct drm_device *dev, +static unsigned int ilk_compute_wm_dirty(struct drm_i915_private *dev_priv, const struct ilk_wm_values *old, const struct ilk_wm_values *new) { @@ -2535,7 +2699,7 @@ static unsigned int ilk_compute_wm_dirty(struct drm_device *dev, enum pipe pipe; int wm_lp; - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { if (old->wm_linetime[pipe] != new->wm_linetime[pipe]) { dirty |= WM_DIRTY_LINETIME(pipe); /* Must disable LP1+ watermarks too */ @@ -2621,7 +2785,7 @@ static void ilk_write_wm_values(struct drm_i915_private *dev_priv, unsigned int dirty; uint32_t val; - dirty = ilk_compute_wm_dirty(dev, previous, results); + dirty = ilk_compute_wm_dirty(dev_priv, previous, results); if (!dirty) return; @@ -3327,13 +3491,18 @@ void valleyview_set_rps(struct drm_device *dev, u8 val) WARN_ON(val > dev_priv->rps.max_freq_softlimit); WARN_ON(val < dev_priv->rps.min_freq_softlimit); - DRM_DEBUG_DRIVER("GPU freq request from %d MHz (%u) to %d MHz (%u)\n", - vlv_gpu_freq(dev_priv, dev_priv->rps.cur_freq), - dev_priv->rps.cur_freq, - vlv_gpu_freq(dev_priv, val), val); + if (WARN_ONCE(IS_CHERRYVIEW(dev) && (val & 1), + "Odd GPU freq value\n")) + val &= ~1; + + if (val != dev_priv->rps.cur_freq) { + DRM_DEBUG_DRIVER("GPU freq request from %d MHz (%u) to %d MHz (%u)\n", + vlv_gpu_freq(dev_priv, dev_priv->rps.cur_freq), + dev_priv->rps.cur_freq, + vlv_gpu_freq(dev_priv, val), val); - if (val != dev_priv->rps.cur_freq) vlv_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, val); + } I915_WRITE(GEN6_PMINTRMSK, gen6_rps_pm_mask(dev_priv, val)); @@ -3406,8 +3575,14 @@ static void valleyview_disable_rps(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + /* we're doing forcewake before Disabling RC6, + * This what the BIOS expects when going into suspend */ + gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL); + I915_WRITE(GEN6_RC_CONTROL, 0); + gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL); + gen6_disable_rps_interrupts(dev); } @@ -3598,7 +3773,6 @@ static void gen6_enable_rps(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_engine_cs *ring; u32 rp_state_cap; - u32 gt_perf_status; u32 rc6vids, pcu_mbox = 0, rc6_mask = 0; u32 gtfifodbg; int rc6_mode; @@ -3623,7 +3797,6 @@ static void gen6_enable_rps(struct drm_device *dev) gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL); rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); - gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS); parse_rp_state_cap(dev_priv, rp_state_cap); @@ -3965,11 +4138,27 @@ static void valleyview_cleanup_pctx(struct drm_device *dev) static void valleyview_init_gt_powersave(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + u32 val; valleyview_setup_pctx(dev); mutex_lock(&dev_priv->rps.hw_lock); + val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); + switch ((val >> 6) & 3) { + case 0: + case 1: + dev_priv->mem_freq = 800; + break; + case 2: + dev_priv->mem_freq = 1066; + break; + case 3: + dev_priv->mem_freq = 1333; + break; + } + DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq); + dev_priv->rps.max_freq = valleyview_rps_max_freq(dev_priv); dev_priv->rps.rp0_freq = dev_priv->rps.max_freq; DRM_DEBUG_DRIVER("max GPU freq: %d MHz (%u)\n", @@ -4004,11 +4193,38 @@ static void valleyview_init_gt_powersave(struct drm_device *dev) static void cherryview_init_gt_powersave(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + u32 val; cherryview_setup_pctx(dev); mutex_lock(&dev_priv->rps.hw_lock); + val = vlv_punit_read(dev_priv, CCK_FUSE_REG); + switch ((val >> 2) & 0x7) { + case 0: + case 1: + dev_priv->rps.cz_freq = 200; + dev_priv->mem_freq = 1600; + break; + case 2: + dev_priv->rps.cz_freq = 267; + dev_priv->mem_freq = 1600; + break; + case 3: + dev_priv->rps.cz_freq = 333; + dev_priv->mem_freq = 2000; + break; + case 4: + dev_priv->rps.cz_freq = 320; + dev_priv->mem_freq = 1600; + break; + case 5: + dev_priv->rps.cz_freq = 400; + dev_priv->mem_freq = 1600; + break; + } + DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq); + dev_priv->rps.max_freq = cherryview_rps_max_freq(dev_priv); dev_priv->rps.rp0_freq = dev_priv->rps.max_freq; DRM_DEBUG_DRIVER("max GPU freq: %d MHz (%u)\n", @@ -4030,6 +4246,12 @@ static void cherryview_init_gt_powersave(struct drm_device *dev) vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq), dev_priv->rps.min_freq); + WARN_ONCE((dev_priv->rps.max_freq | + dev_priv->rps.efficient_freq | + dev_priv->rps.rp1_freq | + dev_priv->rps.min_freq) & 1, + "Odd GPU freq values\n"); + /* Preserve min/max settings in case of re-init */ if (dev_priv->rps.max_freq_softlimit == 0) dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq; @@ -5088,7 +5310,7 @@ static void g4x_disable_trickle_feed(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; int pipe; - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { I915_WRITE(DSPCNTR(pipe), I915_READ(DSPCNTR(pipe)) | DISPPLANE_TRICKLE_FEED_DISABLE); @@ -5203,7 +5425,7 @@ static void cpt_init_clock_gating(struct drm_device *dev) /* The below fixes the weird display corruption, a few pixels shifted * downward, on (only) LVDS of some HP laptops with IVY. */ - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { val = I915_READ(TRANS_CHICKEN2(pipe)); val |= TRANS_CHICKEN2_TIMING_OVERRIDE; val &= ~TRANS_CHICKEN2_FDI_POLARITY_REVERSED; @@ -5215,7 +5437,7 @@ static void cpt_init_clock_gating(struct drm_device *dev) I915_WRITE(TRANS_CHICKEN2(pipe), val); } /* WADP0ClockGatingDisable */ - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { I915_WRITE(TRANS_CHICKEN1(pipe), TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); } @@ -5383,7 +5605,7 @@ static void lpt_suspend_hw(struct drm_device *dev) } } -static void gen8_init_clock_gating(struct drm_device *dev) +static void broadwell_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; enum pipe pipe; @@ -5395,37 +5617,12 @@ static void gen8_init_clock_gating(struct drm_device *dev) /* FIXME(BDW): Check all the w/a, some might only apply to * pre-production hw. */ - /* WaDisablePartialInstShootdown:bdw */ - I915_WRITE(GEN8_ROW_CHICKEN, - _MASKED_BIT_ENABLE(PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE)); - /* WaDisableThreadStallDopClockGating:bdw */ - /* FIXME: Unclear whether we really need this on production bdw. */ - I915_WRITE(GEN8_ROW_CHICKEN, - _MASKED_BIT_ENABLE(STALL_DOP_GATING_DISABLE)); - - /* - * This GEN8_CENTROID_PIXEL_OPT_DIS W/A is only needed for - * pre-production hardware - */ - I915_WRITE(HALF_SLICE_CHICKEN3, - _MASKED_BIT_ENABLE(GEN8_CENTROID_PIXEL_OPT_DIS)); - I915_WRITE(HALF_SLICE_CHICKEN3, - _MASKED_BIT_ENABLE(GEN8_SAMPLER_POWER_BYPASS_DIS)); I915_WRITE(GAMTARBMODE, _MASKED_BIT_ENABLE(ARB_MODE_BWGTLB_DISABLE)); I915_WRITE(_3D_CHICKEN3, _MASKED_BIT_ENABLE(_3D_CHICKEN_SDE_LIMIT_FIFO_POLY_DEPTH(2))); - I915_WRITE(COMMON_SLICE_CHICKEN2, - _MASKED_BIT_ENABLE(GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE)); - - I915_WRITE(GEN7_HALF_SLICE_CHICKEN1, - _MASKED_BIT_ENABLE(GEN7_SINGLE_SUBSCAN_DISPATCH_ENABLE)); - - /* WaDisableDopClockGating:bdw May not be needed for production */ - I915_WRITE(GEN7_ROW_CHICKEN2, - _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); /* WaSwitchSolVfFArbitrationPriority:bdw */ I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | HSW_ECOCHK_ARB_PRIO_SOL); @@ -5435,37 +5632,18 @@ static void gen8_init_clock_gating(struct drm_device *dev) I915_READ(CHICKEN_PAR1_1) | DPA_MASK_VBLANK_SRD); /* WaPsrDPRSUnmaskVBlankInSRD:bdw */ - for_each_pipe(pipe) { + for_each_pipe(dev_priv, pipe) { I915_WRITE(CHICKEN_PIPESL_1(pipe), I915_READ(CHICKEN_PIPESL_1(pipe)) | BDW_DPRS_MASK_VBLANK_SRD); } - /* Use Force Non-Coherent whenever executing a 3D context. This is a - * workaround for for a possible hang in the unlikely event a TLB - * invalidation occurs during a PSD flush. - */ - I915_WRITE(HDC_CHICKEN0, - I915_READ(HDC_CHICKEN0) | - _MASKED_BIT_ENABLE(HDC_FORCE_NON_COHERENT)); - /* WaVSRefCountFullforceMissDisable:bdw */ /* WaDSRefCountFullforceMissDisable:bdw */ I915_WRITE(GEN7_FF_THREAD_MODE, I915_READ(GEN7_FF_THREAD_MODE) & ~(GEN8_FF_DS_REF_CNT_FFME | GEN7_FF_VS_REF_CNT_FFME)); - /* - * BSpec recommends 8x4 when MSAA is used, - * however in practice 16x4 seems fastest. - * - * Note that PS/WM thread counts depend on the WIZ hashing - * disable bit, which we don't touch here, but it's good - * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM). - */ - I915_WRITE(GEN7_GT_MODE, - GEN6_WIZ_HASHING_MASK | GEN6_WIZ_HASHING_16x4); - I915_WRITE(GEN6_RC_SLEEP_PSMI_CONTROL, _MASKED_BIT_ENABLE(GEN8_RC_SEMA_IDLE_MSG_DISABLE)); @@ -5473,9 +5651,7 @@ static void gen8_init_clock_gating(struct drm_device *dev) I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) | GEN8_SDEUNIT_CLOCK_GATE_DISABLE); - /* Wa4x4STCOptimizationDisable:bdw */ - I915_WRITE(CACHE_MODE_1, - _MASKED_BIT_ENABLE(GEN8_4x4_STC_OPTIMIZATION_DISABLE)); + lpt_init_clock_gating(dev); } static void haswell_init_clock_gating(struct drm_device *dev) @@ -5631,24 +5807,6 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) static void valleyview_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 val; - - mutex_lock(&dev_priv->rps.hw_lock); - val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); - mutex_unlock(&dev_priv->rps.hw_lock); - switch ((val >> 6) & 3) { - case 0: - case 1: - dev_priv->mem_freq = 800; - break; - case 2: - dev_priv->mem_freq = 1066; - break; - case 3: - dev_priv->mem_freq = 1333; - break; - } - DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq); I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE); @@ -5724,48 +5882,11 @@ static void valleyview_init_clock_gating(struct drm_device *dev) static void cherryview_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 val; - - mutex_lock(&dev_priv->rps.hw_lock); - val = vlv_punit_read(dev_priv, CCK_FUSE_REG); - mutex_unlock(&dev_priv->rps.hw_lock); - switch ((val >> 2) & 0x7) { - case 0: - case 1: - dev_priv->rps.cz_freq = CHV_CZ_CLOCK_FREQ_MODE_200; - dev_priv->mem_freq = 1600; - break; - case 2: - dev_priv->rps.cz_freq = CHV_CZ_CLOCK_FREQ_MODE_267; - dev_priv->mem_freq = 1600; - break; - case 3: - dev_priv->rps.cz_freq = CHV_CZ_CLOCK_FREQ_MODE_333; - dev_priv->mem_freq = 2000; - break; - case 4: - dev_priv->rps.cz_freq = CHV_CZ_CLOCK_FREQ_MODE_320; - dev_priv->mem_freq = 1600; - break; - case 5: - dev_priv->rps.cz_freq = CHV_CZ_CLOCK_FREQ_MODE_400; - dev_priv->mem_freq = 1600; - break; - } - DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq); I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE); I915_WRITE(MI_ARB_VLV, MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE); - /* WaDisablePartialInstShootdown:chv */ - I915_WRITE(GEN8_ROW_CHICKEN, - _MASKED_BIT_ENABLE(PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE)); - - /* WaDisableThreadStallDopClockGating:chv */ - I915_WRITE(GEN8_ROW_CHICKEN, - _MASKED_BIT_ENABLE(STALL_DOP_GATING_DISABLE)); - /* WaVSRefCountFullforceMissDisable:chv */ /* WaDSRefCountFullforceMissDisable:chv */ I915_WRITE(GEN7_FF_THREAD_MODE, @@ -5784,10 +5905,6 @@ static void cherryview_init_clock_gating(struct drm_device *dev) I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) | GEN8_SDEUNIT_CLOCK_GATE_DISABLE); - /* WaDisableSamplerPowerBypass:chv (pre-production hw) */ - I915_WRITE(HALF_SLICE_CHICKEN3, - _MASKED_BIT_ENABLE(GEN8_SAMPLER_POWER_BYPASS_DIS)); - /* WaDisableGunitClockGating:chv (pre-production hw) */ I915_WRITE(VLV_GUNIT_CLOCK_GATE, I915_READ(VLV_GUNIT_CLOCK_GATE) | GINT_DIS); @@ -5797,8 +5914,6 @@ static void cherryview_init_clock_gating(struct drm_device *dev) _MASKED_BIT_ENABLE(GEN8_FF_DOP_CLOCK_GATE_DISABLE)); /* WaDisableDopClockGating:chv (pre-production hw) */ - I915_WRITE(GEN7_ROW_CHICKEN2, - _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); I915_WRITE(GEN6_UCGCTL1, I915_READ(GEN6_UCGCTL1) | GEN6_EU_TCUNIT_CLOCK_GATE_DISABLE); } @@ -5883,6 +5998,9 @@ static void gen3_init_clock_gating(struct drm_device *dev) /* On GEN3 we really need to make sure the ARB C3 LP bit is set */ I915_WRITE(MI_ARB_STATE, _MASKED_BIT_ENABLE(MI_ARB_C3_LP_WRITE_ENABLE)); + + I915_WRITE(MI_ARB_STATE, + _MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE)); } static void i85x_init_clock_gating(struct drm_device *dev) @@ -5894,6 +6012,9 @@ static void i85x_init_clock_gating(struct drm_device *dev) /* interrupts should cause a wake up from C3 */ I915_WRITE(MI_STATE, _MASKED_BIT_ENABLE(MI_AGPBUSY_INT_EN) | _MASKED_BIT_DISABLE(MI_AGPBUSY_830_MODE)); + + I915_WRITE(MEM_MODE, + _MASKED_BIT_ENABLE(MEM_DISPLAY_TRICKLE_FEED_DISABLE)); } static void i830_init_clock_gating(struct drm_device *dev) @@ -5901,6 +6022,10 @@ static void i830_init_clock_gating(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; I915_WRITE(DSPCLK_GATE_D, OVRUNIT_CLOCK_GATE_DISABLE); + + I915_WRITE(MEM_MODE, + _MASKED_BIT_ENABLE(MEM_DISPLAY_A_TRICKLE_FEED_DISABLE) | + _MASKED_BIT_ENABLE(MEM_DISPLAY_B_TRICKLE_FEED_DISABLE)); } void intel_init_clock_gating(struct drm_device *dev) @@ -6203,6 +6328,8 @@ static void vlv_display_power_well_disable(struct drm_i915_private *dev_priv, spin_unlock_irq(&dev_priv->irq_lock); vlv_set_power_well(dev_priv, power_well, false); + + vlv_power_sequencer_reset(dev_priv); } static void vlv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, @@ -6238,12 +6365,11 @@ static void vlv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, static void vlv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, struct i915_power_well *power_well) { - struct drm_device *dev = dev_priv->dev; enum pipe pipe; WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC); - for_each_pipe(pipe) + for_each_pipe(dev_priv, pipe) assert_pll_disabled(dev_priv, pipe); /* Assert common reset */ @@ -6252,6 +6378,153 @@ static void vlv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, vlv_set_power_well(dev_priv, power_well, false); } +static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + enum dpio_phy phy; + + WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC && + power_well->data != PUNIT_POWER_WELL_DPIO_CMN_D); + + /* + * Enable the CRI clock source so we can get at the + * display and the reference clock for VGA + * hotplug / manual detection. + */ + if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) { + phy = DPIO_PHY0; + I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) | + DPLL_REFA_CLK_ENABLE_VLV); + I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) | + DPLL_REFA_CLK_ENABLE_VLV | DPLL_INTEGRATED_CRI_CLK_VLV); + } else { + phy = DPIO_PHY1; + I915_WRITE(DPLL(PIPE_C), I915_READ(DPLL(PIPE_C)) | + DPLL_REFA_CLK_ENABLE_VLV | DPLL_INTEGRATED_CRI_CLK_VLV); + } + udelay(1); /* >10ns for cmnreset, >0ns for sidereset */ + vlv_set_power_well(dev_priv, power_well, true); + + /* Poll for phypwrgood signal */ + if (wait_for(I915_READ(DISPLAY_PHY_STATUS) & PHY_POWERGOOD(phy), 1)) + DRM_ERROR("Display PHY %d is not power up\n", phy); + + I915_WRITE(DISPLAY_PHY_CONTROL, I915_READ(DISPLAY_PHY_CONTROL) | + PHY_COM_LANE_RESET_DEASSERT(phy)); +} + +static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + enum dpio_phy phy; + + WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC && + power_well->data != PUNIT_POWER_WELL_DPIO_CMN_D); + + if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) { + phy = DPIO_PHY0; + assert_pll_disabled(dev_priv, PIPE_A); + assert_pll_disabled(dev_priv, PIPE_B); + } else { + phy = DPIO_PHY1; + assert_pll_disabled(dev_priv, PIPE_C); + } + + I915_WRITE(DISPLAY_PHY_CONTROL, I915_READ(DISPLAY_PHY_CONTROL) & + ~PHY_COM_LANE_RESET_DEASSERT(phy)); + + vlv_set_power_well(dev_priv, power_well, false); +} + +static bool chv_pipe_power_well_enabled(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + enum pipe pipe = power_well->data; + bool enabled; + u32 state, ctrl; + + mutex_lock(&dev_priv->rps.hw_lock); + + state = vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ) & DP_SSS_MASK(pipe); + /* + * We only ever set the power-on and power-gate states, anything + * else is unexpected. + */ + WARN_ON(state != DP_SSS_PWR_ON(pipe) && state != DP_SSS_PWR_GATE(pipe)); + enabled = state == DP_SSS_PWR_ON(pipe); + + /* + * A transient state at this point would mean some unexpected party + * is poking at the power controls too. + */ + ctrl = vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ) & DP_SSC_MASK(pipe); + WARN_ON(ctrl << 16 != state); + + mutex_unlock(&dev_priv->rps.hw_lock); + + return enabled; +} + +static void chv_set_pipe_power_well(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well, + bool enable) +{ + enum pipe pipe = power_well->data; + u32 state; + u32 ctrl; + + state = enable ? DP_SSS_PWR_ON(pipe) : DP_SSS_PWR_GATE(pipe); + + mutex_lock(&dev_priv->rps.hw_lock); + +#define COND \ + ((vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ) & DP_SSS_MASK(pipe)) == state) + + if (COND) + goto out; + + ctrl = vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ); + ctrl &= ~DP_SSC_MASK(pipe); + ctrl |= enable ? DP_SSC_PWR_ON(pipe) : DP_SSC_PWR_GATE(pipe); + vlv_punit_write(dev_priv, PUNIT_REG_DSPFREQ, ctrl); + + if (wait_for(COND, 100)) + DRM_ERROR("timout setting power well state %08x (%08x)\n", + state, + vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ)); + +#undef COND + +out: + mutex_unlock(&dev_priv->rps.hw_lock); +} + +static void chv_pipe_power_well_sync_hw(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + chv_set_pipe_power_well(dev_priv, power_well, power_well->count > 0); +} + +static void chv_pipe_power_well_enable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + WARN_ON_ONCE(power_well->data != PIPE_A && + power_well->data != PIPE_B && + power_well->data != PIPE_C); + + chv_set_pipe_power_well(dev_priv, power_well, true); +} + +static void chv_pipe_power_well_disable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + WARN_ON_ONCE(power_well->data != PIPE_A && + power_well->data != PIPE_B && + power_well->data != PIPE_C); + + chv_set_pipe_power_well(dev_priv, power_well, false); +} + static void check_power_well_state(struct drm_i915_private *dev_priv, struct i915_power_well *power_well) { @@ -6443,6 +6716,39 @@ EXPORT_SYMBOL_GPL(i915_get_cdclk_freq); BIT(POWER_DOMAIN_PORT_DDI_C_4_LANES) | \ BIT(POWER_DOMAIN_INIT)) +#define CHV_PIPE_A_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_PIPE_A) | \ + BIT(POWER_DOMAIN_INIT)) + +#define CHV_PIPE_B_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_PIPE_B) | \ + BIT(POWER_DOMAIN_INIT)) + +#define CHV_PIPE_C_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_PIPE_C) | \ + BIT(POWER_DOMAIN_INIT)) + +#define CHV_DPIO_CMN_BC_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_PORT_DDI_B_2_LANES) | \ + BIT(POWER_DOMAIN_PORT_DDI_B_4_LANES) | \ + BIT(POWER_DOMAIN_PORT_DDI_C_2_LANES) | \ + BIT(POWER_DOMAIN_PORT_DDI_C_4_LANES) | \ + BIT(POWER_DOMAIN_INIT)) + +#define CHV_DPIO_CMN_D_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_PORT_DDI_D_2_LANES) | \ + BIT(POWER_DOMAIN_PORT_DDI_D_4_LANES) | \ + BIT(POWER_DOMAIN_INIT)) + +#define CHV_DPIO_TX_D_LANES_01_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_PORT_DDI_D_2_LANES) | \ + BIT(POWER_DOMAIN_PORT_DDI_D_4_LANES) | \ + BIT(POWER_DOMAIN_INIT)) + +#define CHV_DPIO_TX_D_LANES_23_POWER_DOMAINS ( \ + BIT(POWER_DOMAIN_PORT_DDI_D_4_LANES) | \ + BIT(POWER_DOMAIN_INIT)) + static const struct i915_power_well_ops i9xx_always_on_power_well_ops = { .sync_hw = i9xx_always_on_power_well_noop, .enable = i9xx_always_on_power_well_noop, @@ -6450,6 +6756,20 @@ static const struct i915_power_well_ops i9xx_always_on_power_well_ops = { .is_enabled = i9xx_always_on_power_well_enabled, }; +static const struct i915_power_well_ops chv_pipe_power_well_ops = { + .sync_hw = chv_pipe_power_well_sync_hw, + .enable = chv_pipe_power_well_enable, + .disable = chv_pipe_power_well_disable, + .is_enabled = chv_pipe_power_well_enabled, +}; + +static const struct i915_power_well_ops chv_dpio_cmn_power_well_ops = { + .sync_hw = vlv_power_well_sync_hw, + .enable = chv_dpio_cmn_power_well_enable, + .disable = chv_dpio_cmn_power_well_disable, + .is_enabled = vlv_power_well_enabled, +}; + static struct i915_power_well i9xx_always_on_power_well[] = { { .name = "always-on", @@ -6572,6 +6892,107 @@ static struct i915_power_well vlv_power_wells[] = { }, }; +static struct i915_power_well chv_power_wells[] = { + { + .name = "always-on", + .always_on = 1, + .domains = VLV_ALWAYS_ON_POWER_DOMAINS, + .ops = &i9xx_always_on_power_well_ops, + }, +#if 0 + { + .name = "display", + .domains = VLV_DISPLAY_POWER_DOMAINS, + .data = PUNIT_POWER_WELL_DISP2D, + .ops = &vlv_display_power_well_ops, + }, + { + .name = "pipe-a", + .domains = CHV_PIPE_A_POWER_DOMAINS, + .data = PIPE_A, + .ops = &chv_pipe_power_well_ops, + }, + { + .name = "pipe-b", + .domains = CHV_PIPE_B_POWER_DOMAINS, + .data = PIPE_B, + .ops = &chv_pipe_power_well_ops, + }, + { + .name = "pipe-c", + .domains = CHV_PIPE_C_POWER_DOMAINS, + .data = PIPE_C, + .ops = &chv_pipe_power_well_ops, + }, +#endif + { + .name = "dpio-common-bc", + /* + * XXX: cmnreset for one PHY seems to disturb the other. + * As a workaround keep both powered on at the same + * time for now. + */ + .domains = CHV_DPIO_CMN_BC_POWER_DOMAINS | CHV_DPIO_CMN_D_POWER_DOMAINS, + .data = PUNIT_POWER_WELL_DPIO_CMN_BC, + .ops = &chv_dpio_cmn_power_well_ops, + }, + { + .name = "dpio-common-d", + /* + * XXX: cmnreset for one PHY seems to disturb the other. + * As a workaround keep both powered on at the same + * time for now. + */ + .domains = CHV_DPIO_CMN_BC_POWER_DOMAINS | CHV_DPIO_CMN_D_POWER_DOMAINS, + .data = PUNIT_POWER_WELL_DPIO_CMN_D, + .ops = &chv_dpio_cmn_power_well_ops, + }, +#if 0 + { + .name = "dpio-tx-b-01", + .domains = VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS | + VLV_DPIO_TX_B_LANES_23_POWER_DOMAINS, + .ops = &vlv_dpio_power_well_ops, + .data = PUNIT_POWER_WELL_DPIO_TX_B_LANES_01, + }, + { + .name = "dpio-tx-b-23", + .domains = VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS | + VLV_DPIO_TX_B_LANES_23_POWER_DOMAINS, + .ops = &vlv_dpio_power_well_ops, + .data = PUNIT_POWER_WELL_DPIO_TX_B_LANES_23, + }, + { + .name = "dpio-tx-c-01", + .domains = VLV_DPIO_TX_C_LANES_01_POWER_DOMAINS | + VLV_DPIO_TX_C_LANES_23_POWER_DOMAINS, + .ops = &vlv_dpio_power_well_ops, + .data = PUNIT_POWER_WELL_DPIO_TX_C_LANES_01, + }, + { + .name = "dpio-tx-c-23", + .domains = VLV_DPIO_TX_C_LANES_01_POWER_DOMAINS | + VLV_DPIO_TX_C_LANES_23_POWER_DOMAINS, + .ops = &vlv_dpio_power_well_ops, + .data = PUNIT_POWER_WELL_DPIO_TX_C_LANES_23, + }, + { + .name = "dpio-tx-d-01", + .domains = CHV_DPIO_TX_D_LANES_01_POWER_DOMAINS | + CHV_DPIO_TX_D_LANES_23_POWER_DOMAINS, + .ops = &vlv_dpio_power_well_ops, + .data = PUNIT_POWER_WELL_DPIO_TX_D_LANES_01, + }, + { + .name = "dpio-tx-d-23", + .domains = CHV_DPIO_TX_D_LANES_01_POWER_DOMAINS | + CHV_DPIO_TX_D_LANES_23_POWER_DOMAINS, + .ops = &vlv_dpio_power_well_ops, + .data = PUNIT_POWER_WELL_DPIO_TX_D_LANES_23, + }, +#endif +}; + static struct i915_power_well *lookup_power_well(struct drm_i915_private *dev_priv, enum punit_power_well power_well_id) { @@ -6608,6 +7029,8 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv) } else if (IS_BROADWELL(dev_priv->dev)) { set_power_wells(power_domains, bdw_power_wells); hsw_pwr = power_domains; + } else if (IS_CHERRYVIEW(dev_priv->dev)) { + set_power_wells(power_domains, chv_power_wells); } else if (IS_VALLEYVIEW(dev_priv->dev)) { set_power_wells(power_domains, vlv_power_wells); } else { @@ -6833,13 +7256,15 @@ void intel_init_pm(struct drm_device *dev) else if (IS_HASWELL(dev)) dev_priv->display.init_clock_gating = haswell_init_clock_gating; else if (INTEL_INFO(dev)->gen == 8) - dev_priv->display.init_clock_gating = gen8_init_clock_gating; + dev_priv->display.init_clock_gating = broadwell_init_clock_gating; } else if (IS_CHERRYVIEW(dev)) { - dev_priv->display.update_wm = valleyview_update_wm; + dev_priv->display.update_wm = cherryview_update_wm; + dev_priv->display.update_sprite_wm = valleyview_update_sprite_wm; dev_priv->display.init_clock_gating = cherryview_init_clock_gating; } else if (IS_VALLEYVIEW(dev)) { dev_priv->display.update_wm = valleyview_update_wm; + dev_priv->display.update_sprite_wm = valleyview_update_sprite_wm; dev_priv->display.init_clock_gating = valleyview_init_clock_gating; } else if (IS_PINEVIEW(dev)) { @@ -7025,6 +7450,7 @@ static int chv_freq_opcode(struct drm_i915_private *dev_priv, int val) return -1; } + /* CHV needs even values */ opcode = (DIV_ROUND_CLOSEST((val * 2 * mul), dev_priv->rps.cz_freq) * 2); return opcode; diff --git a/drivers/gpu/drm/i915/intel_renderstate.h b/drivers/gpu/drm/i915/intel_renderstate.h index fd4f66231d30edec7d96236fac145e2b00b7b0c5..6c792d3a9c9caa55a6959868190223b396fd173e 100644 --- a/drivers/gpu/drm/i915/intel_renderstate.h +++ b/drivers/gpu/drm/i915/intel_renderstate.h @@ -24,13 +24,7 @@ #ifndef _INTEL_RENDERSTATE_H #define _INTEL_RENDERSTATE_H -#include - -struct intel_renderstate_rodata { - const u32 *reloc; - const u32 *batch; - const u32 batch_items; -}; +#include "i915_drv.h" extern const struct intel_renderstate_rodata gen6_null_state; extern const struct intel_renderstate_rodata gen7_null_state; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 47a126a0493f811cad30474532477cfe5994734e..0a80e419b5894f1c36a1b6eb0f6078f76fe7c0f4 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -33,14 +33,24 @@ #include "i915_trace.h" #include "intel_drv.h" -/* Early gen2 devices have a cacheline of just 32 bytes, using 64 is overkill, - * but keeps the logic simple. Indeed, the whole purpose of this macro is just - * to give some inclination as to some of the magic values used in the various - * workarounds! - */ -#define CACHELINE_BYTES 64 +bool +intel_ring_initialized(struct intel_engine_cs *ring) +{ + struct drm_device *dev = ring->dev; + + if (!dev) + return false; + + if (i915.enable_execlists) { + struct intel_context *dctx = ring->default_context; + struct intel_ringbuffer *ringbuf = dctx->engine[ring->id].ringbuf; + + return ringbuf->obj; + } else + return ring->buffer && ring->buffer->obj; +} -static inline int __ring_space(int head, int tail, int size) +int __intel_ring_space(int head, int tail, int size) { int space = head - (tail + I915_RING_FREE_SPACE); if (space < 0) @@ -48,12 +58,13 @@ static inline int __ring_space(int head, int tail, int size) return space; } -static inline int ring_space(struct intel_ringbuffer *ringbuf) +int intel_ring_space(struct intel_ringbuffer *ringbuf) { - return __ring_space(ringbuf->head & HEAD_ADDR, ringbuf->tail, ringbuf->size); + return __intel_ring_space(ringbuf->head & HEAD_ADDR, + ringbuf->tail, ringbuf->size); } -static bool intel_ring_stopped(struct intel_engine_cs *ring) +bool intel_ring_stopped(struct intel_engine_cs *ring) { struct drm_i915_private *dev_priv = ring->dev->dev_private; return dev_priv->gpu_error.stop_rings & intel_ring_flag(ring); @@ -433,7 +444,14 @@ gen8_render_ring_flush(struct intel_engine_cs *ring, return ret; } - return gen8_emit_pipe_control(ring, flags, scratch_addr); + ret = gen8_emit_pipe_control(ring, flags, scratch_addr); + if (ret) + return ret; + + if (!invalidate_domains && flush_domains) + return gen7_ring_fbc_flush(ring, FBC_REND_NUKE); + + return 0; } static void ring_write_tail(struct intel_engine_cs *ring, @@ -476,9 +494,14 @@ static bool stop_ring(struct intel_engine_cs *ring) if (!IS_GEN2(ring->dev)) { I915_WRITE_MODE(ring, _MASKED_BIT_ENABLE(STOP_RING)); - if (wait_for_atomic((I915_READ_MODE(ring) & MODE_IDLE) != 0, 1000)) { - DRM_ERROR("%s :timed out trying to stop ring\n", ring->name); - return false; + if (wait_for((I915_READ_MODE(ring) & MODE_IDLE) != 0, 1000)) { + DRM_ERROR("%s : timed out trying to stop ring\n", ring->name); + /* Sometimes we observe that the idle flag is not + * set even though the ring is empty. So double + * check before giving up. + */ + if (I915_READ_HEAD(ring) != I915_READ_TAIL(ring)) + return false; } } @@ -540,6 +563,14 @@ static int init_ring_common(struct intel_engine_cs *ring) * also enforces ordering), otherwise the hw might lose the new ring * register values. */ I915_WRITE_START(ring, i915_gem_obj_ggtt_offset(obj)); + + /* WaClearRingBufHeadRegAtInit:ctg,elk */ + if (I915_READ_HEAD(ring)) + DRM_DEBUG("%s initialization failed [head=%08x], fudging\n", + ring->name, I915_READ_HEAD(ring)); + I915_WRITE_HEAD(ring, 0); + (void)I915_READ_HEAD(ring); + I915_WRITE_CTL(ring, ((ringbuf->size - PAGE_SIZE) & RING_NR_PAGES) | RING_VALID); @@ -563,7 +594,7 @@ static int init_ring_common(struct intel_engine_cs *ring) else { ringbuf->head = I915_READ_HEAD(ring); ringbuf->tail = I915_READ_TAIL(ring) & TAIL_ADDR; - ringbuf->space = ring_space(ringbuf); + ringbuf->space = intel_ring_space(ringbuf); ringbuf->last_retired_head = -1; } @@ -575,8 +606,25 @@ static int init_ring_common(struct intel_engine_cs *ring) return ret; } -static int -init_pipe_control(struct intel_engine_cs *ring) +void +intel_fini_pipe_control(struct intel_engine_cs *ring) +{ + struct drm_device *dev = ring->dev; + + if (ring->scratch.obj == NULL) + return; + + if (INTEL_INFO(dev)->gen >= 5) { + kunmap(sg_page(ring->scratch.obj->pages->sgl)); + i915_gem_object_ggtt_unpin(ring->scratch.obj); + } + + drm_gem_object_unreference(&ring->scratch.obj->base); + ring->scratch.obj = NULL; +} + +int +intel_init_pipe_control(struct intel_engine_cs *ring) { int ret; @@ -617,6 +665,135 @@ init_pipe_control(struct intel_engine_cs *ring) return ret; } +static inline void intel_ring_emit_wa(struct intel_engine_cs *ring, + u32 addr, u32 value) +{ + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + if (WARN_ON(dev_priv->num_wa_regs >= I915_MAX_WA_REGS)) + return; + + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1)); + intel_ring_emit(ring, addr); + intel_ring_emit(ring, value); + + dev_priv->intel_wa_regs[dev_priv->num_wa_regs].addr = addr; + dev_priv->intel_wa_regs[dev_priv->num_wa_regs].mask = value & 0xFFFF; + /* value is updated with the status of remaining bits of this + * register when it is read from debugfs file + */ + dev_priv->intel_wa_regs[dev_priv->num_wa_regs].value = value; + dev_priv->num_wa_regs++; + + return; +} + +static int bdw_init_workarounds(struct intel_engine_cs *ring) +{ + int ret; + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + /* + * workarounds applied in this fn are part of register state context, + * they need to be re-initialized followed by gpu reset, suspend/resume, + * module reload. + */ + dev_priv->num_wa_regs = 0; + memset(dev_priv->intel_wa_regs, 0, sizeof(dev_priv->intel_wa_regs)); + + /* + * update the number of dwords required based on the + * actual number of workarounds applied + */ + ret = intel_ring_begin(ring, 18); + if (ret) + return ret; + + /* WaDisablePartialInstShootdown:bdw */ + /* WaDisableThreadStallDopClockGating:bdw */ + /* FIXME: Unclear whether we really need this on production bdw. */ + intel_ring_emit_wa(ring, GEN8_ROW_CHICKEN, + _MASKED_BIT_ENABLE(PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE + | STALL_DOP_GATING_DISABLE)); + + /* WaDisableDopClockGating:bdw May not be needed for production */ + intel_ring_emit_wa(ring, GEN7_ROW_CHICKEN2, + _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); + + intel_ring_emit_wa(ring, HALF_SLICE_CHICKEN3, + _MASKED_BIT_ENABLE(GEN8_SAMPLER_POWER_BYPASS_DIS)); + + /* Use Force Non-Coherent whenever executing a 3D context. This is a + * workaround for for a possible hang in the unlikely event a TLB + * invalidation occurs during a PSD flush. + */ + intel_ring_emit_wa(ring, HDC_CHICKEN0, + _MASKED_BIT_ENABLE(HDC_FORCE_NON_COHERENT)); + + /* Wa4x4STCOptimizationDisable:bdw */ + intel_ring_emit_wa(ring, CACHE_MODE_1, + _MASKED_BIT_ENABLE(GEN8_4x4_STC_OPTIMIZATION_DISABLE)); + + /* + * BSpec recommends 8x4 when MSAA is used, + * however in practice 16x4 seems fastest. + * + * Note that PS/WM thread counts depend on the WIZ hashing + * disable bit, which we don't touch here, but it's good + * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM). + */ + intel_ring_emit_wa(ring, GEN7_GT_MODE, + GEN6_WIZ_HASHING_MASK | GEN6_WIZ_HASHING_16x4); + + intel_ring_advance(ring); + + DRM_DEBUG_DRIVER("Number of Workarounds applied: %d\n", + dev_priv->num_wa_regs); + + return 0; +} + +static int chv_init_workarounds(struct intel_engine_cs *ring) +{ + int ret; + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + /* + * workarounds applied in this fn are part of register state context, + * they need to be re-initialized followed by gpu reset, suspend/resume, + * module reload. + */ + dev_priv->num_wa_regs = 0; + memset(dev_priv->intel_wa_regs, 0, sizeof(dev_priv->intel_wa_regs)); + + ret = intel_ring_begin(ring, 12); + if (ret) + return ret; + + /* WaDisablePartialInstShootdown:chv */ + intel_ring_emit_wa(ring, GEN8_ROW_CHICKEN, + _MASKED_BIT_ENABLE(PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE)); + + /* WaDisableThreadStallDopClockGating:chv */ + intel_ring_emit_wa(ring, GEN8_ROW_CHICKEN, + _MASKED_BIT_ENABLE(STALL_DOP_GATING_DISABLE)); + + /* WaDisableDopClockGating:chv (pre-production hw) */ + intel_ring_emit_wa(ring, GEN7_ROW_CHICKEN2, + _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); + + /* WaDisableSamplerPowerBypass:chv (pre-production hw) */ + intel_ring_emit_wa(ring, HALF_SLICE_CHICKEN3, + _MASKED_BIT_ENABLE(GEN8_SAMPLER_POWER_BYPASS_DIS)); + + intel_ring_advance(ring); + + return 0; +} + static int init_render_ring(struct intel_engine_cs *ring) { struct drm_device *dev = ring->dev; @@ -651,7 +828,7 @@ static int init_render_ring(struct intel_engine_cs *ring) _MASKED_BIT_ENABLE(GFX_REPLAY_MODE)); if (INTEL_INFO(dev)->gen >= 5) { - ret = init_pipe_control(ring); + ret = intel_init_pipe_control(ring); if (ret) return ret; } @@ -686,16 +863,7 @@ static void render_ring_cleanup(struct intel_engine_cs *ring) dev_priv->semaphore_obj = NULL; } - if (ring->scratch.obj == NULL) - return; - - if (INTEL_INFO(dev)->gen >= 5) { - kunmap(sg_page(ring->scratch.obj->pages->sgl)); - i915_gem_object_ggtt_unpin(ring->scratch.obj); - } - - drm_gem_object_unreference(&ring->scratch.obj->base); - ring->scratch.obj = NULL; + intel_fini_pipe_control(ring); } static int gen8_rcs_signal(struct intel_engine_cs *signaller, @@ -1526,7 +1694,7 @@ static int init_phys_status_page(struct intel_engine_cs *ring) return 0; } -static void intel_destroy_ringbuffer_obj(struct intel_ringbuffer *ringbuf) +void intel_destroy_ringbuffer_obj(struct intel_ringbuffer *ringbuf) { if (!ringbuf->obj) return; @@ -1537,8 +1705,8 @@ static void intel_destroy_ringbuffer_obj(struct intel_ringbuffer *ringbuf) ringbuf->obj = NULL; } -static int intel_alloc_ringbuffer_obj(struct drm_device *dev, - struct intel_ringbuffer *ringbuf) +int intel_alloc_ringbuffer_obj(struct drm_device *dev, + struct intel_ringbuffer *ringbuf) { struct drm_i915_private *dev_priv = to_i915(dev); struct drm_i915_gem_object *obj; @@ -1600,7 +1768,9 @@ static int intel_init_ring_buffer(struct drm_device *dev, ring->dev = dev; INIT_LIST_HEAD(&ring->active_list); INIT_LIST_HEAD(&ring->request_list); + INIT_LIST_HEAD(&ring->execlist_queue); ringbuf->size = 32 * PAGE_SIZE; + ringbuf->ring = ring; memset(ring->semaphore.sync_seqno, 0, sizeof(ring->semaphore.sync_seqno)); init_waitqueue_head(&ring->irq_queue); @@ -1683,13 +1853,14 @@ static int intel_ring_wait_request(struct intel_engine_cs *ring, int n) ringbuf->head = ringbuf->last_retired_head; ringbuf->last_retired_head = -1; - ringbuf->space = ring_space(ringbuf); + ringbuf->space = intel_ring_space(ringbuf); if (ringbuf->space >= n) return 0; } list_for_each_entry(request, &ring->request_list, list) { - if (__ring_space(request->tail, ringbuf->tail, ringbuf->size) >= n) { + if (__intel_ring_space(request->tail, ringbuf->tail, + ringbuf->size) >= n) { seqno = request->seqno; break; } @@ -1706,7 +1877,7 @@ static int intel_ring_wait_request(struct intel_engine_cs *ring, int n) ringbuf->head = ringbuf->last_retired_head; ringbuf->last_retired_head = -1; - ringbuf->space = ring_space(ringbuf); + ringbuf->space = intel_ring_space(ringbuf); return 0; } @@ -1735,7 +1906,7 @@ static int ring_wait_for_space(struct intel_engine_cs *ring, int n) trace_i915_ring_wait_begin(ring); do { ringbuf->head = I915_READ_HEAD(ring); - ringbuf->space = ring_space(ringbuf); + ringbuf->space = intel_ring_space(ringbuf); if (ringbuf->space >= n) { ret = 0; break; @@ -1787,7 +1958,7 @@ static int intel_wrap_ring_buffer(struct intel_engine_cs *ring) iowrite32(MI_NOOP, virt++); ringbuf->tail = 0; - ringbuf->space = ring_space(ringbuf); + ringbuf->space = intel_ring_space(ringbuf); return 0; } @@ -1992,9 +2163,7 @@ gen8_ring_dispatch_execbuffer(struct intel_engine_cs *ring, u64 offset, u32 len, unsigned flags) { - struct drm_i915_private *dev_priv = ring->dev->dev_private; - bool ppgtt = dev_priv->mm.aliasing_ppgtt != NULL && - !(flags & I915_DISPATCH_SECURE); + bool ppgtt = USES_PPGTT(ring->dev) && !(flags & I915_DISPATCH_SECURE); int ret; ret = intel_ring_begin(ring, 4); @@ -2023,8 +2192,9 @@ hsw_ring_dispatch_execbuffer(struct intel_engine_cs *ring, return ret; intel_ring_emit(ring, - MI_BATCH_BUFFER_START | MI_BATCH_PPGTT_HSW | - (flags & I915_DISPATCH_SECURE ? 0 : MI_BATCH_NON_SECURE_HSW)); + MI_BATCH_BUFFER_START | + (flags & I915_DISPATCH_SECURE ? + 0 : MI_BATCH_PPGTT_HSW | MI_BATCH_NON_SECURE_HSW)); /* bit0-7 is the length on GEN6+ */ intel_ring_emit(ring, offset); intel_ring_advance(ring); @@ -2123,6 +2293,10 @@ int intel_init_render_ring_buffer(struct drm_device *dev) dev_priv->semaphore_obj = obj; } } + if (IS_CHERRYVIEW(dev)) + ring->init_context = chv_init_workarounds; + else + ring->init_context = bdw_init_workarounds; ring->add_request = gen6_add_request; ring->flush = gen8_render_ring_flush; ring->irq_get = gen8_ring_get_irq; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 70525d0c2c74650ef780a69c797c59316cedd371..96479c89f4bda9c2e6af0084e75b6785eee6ccfa 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -5,6 +5,13 @@ #define I915_CMD_HASH_ORDER 9 +/* Early gen2 devices have a cacheline of just 32 bytes, using 64 is overkill, + * but keeps the logic simple. Indeed, the whole purpose of this macro is just + * to give some inclination as to some of the magic values used in the various + * workarounds! + */ +#define CACHELINE_BYTES 64 + /* * Gen2 BSpec "1. Programming Environment" / 1.4.4.6 "Ring Buffer Use" * Gen3 BSpec "vol1c Memory Interface Functions" / 2.3.4.5 "Ring Buffer Use" @@ -90,6 +97,15 @@ struct intel_ringbuffer { struct drm_i915_gem_object *obj; void __iomem *virtual_start; + struct intel_engine_cs *ring; + + /* + * FIXME: This backpointer is an artifact of the history of how the + * execlist patches came into being. It will get removed once the basic + * code has landed. + */ + struct intel_context *FIXME_lrc_ctx; + u32 head; u32 tail; int space; @@ -132,6 +148,8 @@ struct intel_engine_cs { int (*init)(struct intel_engine_cs *ring); + int (*init_context)(struct intel_engine_cs *ring); + void (*write_tail)(struct intel_engine_cs *ring, u32 value); int __must_check (*flush)(struct intel_engine_cs *ring, @@ -214,6 +232,18 @@ struct intel_engine_cs { unsigned int num_dwords); } semaphore; + /* Execlists */ + spinlock_t execlist_lock; + struct list_head execlist_queue; + u8 next_context_status_buffer; + u32 irq_keep_mask; /* bitmask for interrupts that should not be masked */ + int (*emit_request)(struct intel_ringbuffer *ringbuf); + int (*emit_flush)(struct intel_ringbuffer *ringbuf, + u32 invalidate_domains, + u32 flush_domains); + int (*emit_bb_start)(struct intel_ringbuffer *ringbuf, + u64 offset, unsigned flags); + /** * List of objects currently involved in rendering from the * ringbuffer. @@ -287,11 +317,7 @@ struct intel_engine_cs { u32 (*get_cmd_length_mask)(u32 cmd_header); }; -static inline bool -intel_ring_initialized(struct intel_engine_cs *ring) -{ - return ring->buffer && ring->buffer->obj; -} +bool intel_ring_initialized(struct intel_engine_cs *ring); static inline unsigned intel_ring_flag(struct intel_engine_cs *ring) @@ -355,6 +381,10 @@ intel_write_status_page(struct intel_engine_cs *ring, #define I915_GEM_HWS_SCRATCH_INDEX 0x30 #define I915_GEM_HWS_SCRATCH_ADDR (I915_GEM_HWS_SCRATCH_INDEX << MI_STORE_DWORD_INDEX_SHIFT) +void intel_destroy_ringbuffer_obj(struct intel_ringbuffer *ringbuf); +int intel_alloc_ringbuffer_obj(struct drm_device *dev, + struct intel_ringbuffer *ringbuf); + void intel_stop_ring_buffer(struct intel_engine_cs *ring); void intel_cleanup_ring_buffer(struct intel_engine_cs *ring); @@ -372,6 +402,9 @@ static inline void intel_ring_advance(struct intel_engine_cs *ring) struct intel_ringbuffer *ringbuf = ring->buffer; ringbuf->tail &= ringbuf->size - 1; } +int __intel_ring_space(int head, int tail, int size); +int intel_ring_space(struct intel_ringbuffer *ringbuf); +bool intel_ring_stopped(struct intel_engine_cs *ring); void __intel_ring_advance(struct intel_engine_cs *ring); int __must_check intel_ring_idle(struct intel_engine_cs *ring); @@ -379,6 +412,9 @@ void intel_ring_init_seqno(struct intel_engine_cs *ring, u32 seqno); int intel_ring_flush_all_caches(struct intel_engine_cs *ring); int intel_ring_invalidate_all_caches(struct intel_engine_cs *ring); +void intel_fini_pipe_control(struct intel_engine_cs *ring); +int intel_init_pipe_control(struct intel_engine_cs *ring); + int intel_init_render_ring_buffer(struct drm_device *dev); int intel_init_bsd_ring_buffer(struct drm_device *dev); int intel_init_bsd2_ring_buffer(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 168c6652cda198731a2afa7d2675658f86ff6023..07a74ef589bd00636896510d245d6e149a49d653 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -53,6 +53,7 @@ static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl enum pipe pipe = crtc->pipe; long timeout = msecs_to_jiffies_timeout(1); int scanline, min, max, vblank_start; + wait_queue_head_t *wq = drm_crtc_vblank_waitqueue(&crtc->base); DEFINE_WAIT(wait); WARN_ON(!drm_modeset_is_locked(&crtc->base.mutex)); @@ -81,7 +82,7 @@ static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl * other CPUs can see the task state update by the time we * read the scanline. */ - prepare_to_wait(&crtc->vbl_wait, &wait, TASK_UNINTERRUPTIBLE); + prepare_to_wait(wq, &wait, TASK_UNINTERRUPTIBLE); scanline = intel_get_crtc_scanline(crtc); if (scanline < min || scanline > max) @@ -100,7 +101,7 @@ static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl local_irq_disable(); } - finish_wait(&crtc->vbl_wait, &wait); + finish_wait(wq, &wait); drm_vblank_put(dev, pipe); @@ -163,6 +164,7 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc, sprctl &= ~SP_PIXFORMAT_MASK; sprctl &= ~SP_YUV_BYTE_ORDER_MASK; sprctl &= ~SP_TILED; + sprctl &= ~SP_ROTATE_180; switch (fb->pixel_format) { case DRM_FORMAT_YUYV: @@ -235,6 +237,14 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc, fb->pitches[0]); linear_offset -= sprsurf_offset; + if (intel_plane->rotation == BIT(DRM_ROTATE_180)) { + sprctl |= SP_ROTATE_180; + + x += src_w; + y += src_h; + linear_offset += src_h * fb->pitches[0] + src_w * pixel_size; + } + atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count); intel_update_primary_plane(intel_crtc); @@ -364,6 +374,7 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, sprctl &= ~SPRITE_RGB_ORDER_RGBX; sprctl &= ~SPRITE_YUV_BYTE_ORDER_MASK; sprctl &= ~SPRITE_TILED; + sprctl &= ~SPRITE_ROTATE_180; switch (fb->pixel_format) { case DRM_FORMAT_XBGR8888: @@ -426,6 +437,18 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, pixel_size, fb->pitches[0]); linear_offset -= sprsurf_offset; + if (intel_plane->rotation == BIT(DRM_ROTATE_180)) { + sprctl |= SPRITE_ROTATE_180; + + /* HSW and BDW does this automagically in hardware */ + if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) { + x += src_w; + y += src_h; + linear_offset += src_h * fb->pitches[0] + + src_w * pixel_size; + } + } + atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count); intel_update_primary_plane(intel_crtc); @@ -571,6 +594,7 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, dvscntr &= ~DVS_RGB_ORDER_XBGR; dvscntr &= ~DVS_YUV_BYTE_ORDER_MASK; dvscntr &= ~DVS_TILED; + dvscntr &= ~DVS_ROTATE_180; switch (fb->pixel_format) { case DRM_FORMAT_XBGR8888: @@ -628,6 +652,14 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, pixel_size, fb->pitches[0]); linear_offset -= dvssurf_offset; + if (intel_plane->rotation == BIT(DRM_ROTATE_180)) { + dvscntr |= DVS_ROTATE_180; + + x += src_w; + y += src_h; + linear_offset += src_h * fb->pitches[0] + src_w * pixel_size; + } + atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count); intel_update_primary_plane(intel_crtc); @@ -895,6 +927,9 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, max_scale = intel_plane->max_downscale << 16; min_scale = intel_plane->can_scale ? 1 : (1 << 16); + drm_rect_rotate(&src, fb->width << 16, fb->height << 16, + intel_plane->rotation); + hscale = drm_rect_calc_hscale_relaxed(&src, &dst, min_scale, max_scale); BUG_ON(hscale < 0); @@ -933,6 +968,9 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, drm_rect_width(&dst) * hscale - drm_rect_width(&src), drm_rect_height(&dst) * vscale - drm_rect_height(&src)); + drm_rect_rotate_inv(&src, fb->width << 16, fb->height << 16, + intel_plane->rotation); + /* sanity check to make sure the src viewport wasn't enlarged */ WARN_ON(src.x1 < (int) src_x || src.y1 < (int) src_y || @@ -1180,18 +1218,45 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data, return ret; } -void intel_plane_restore(struct drm_plane *plane) +int intel_plane_set_property(struct drm_plane *plane, + struct drm_property *prop, + uint64_t val) +{ + struct drm_device *dev = plane->dev; + struct intel_plane *intel_plane = to_intel_plane(plane); + uint64_t old_val; + int ret = -ENOENT; + + if (prop == dev->mode_config.rotation_property) { + /* exactly one rotation angle please */ + if (hweight32(val & 0xf) != 1) + return -EINVAL; + + if (intel_plane->rotation == val) + return 0; + + old_val = intel_plane->rotation; + intel_plane->rotation = val; + ret = intel_plane_restore(plane); + if (ret) + intel_plane->rotation = old_val; + } + + return ret; +} + +int intel_plane_restore(struct drm_plane *plane) { struct intel_plane *intel_plane = to_intel_plane(plane); if (!plane->crtc || !plane->fb) - return; + return 0; - intel_update_plane(plane, plane->crtc, plane->fb, - intel_plane->crtc_x, intel_plane->crtc_y, - intel_plane->crtc_w, intel_plane->crtc_h, - intel_plane->src_x, intel_plane->src_y, - intel_plane->src_w, intel_plane->src_h); + return plane->funcs->update_plane(plane, plane->crtc, plane->fb, + intel_plane->crtc_x, intel_plane->crtc_y, + intel_plane->crtc_w, intel_plane->crtc_h, + intel_plane->src_x, intel_plane->src_y, + intel_plane->src_w, intel_plane->src_h); } void intel_plane_disable(struct drm_plane *plane) @@ -1206,6 +1271,7 @@ static const struct drm_plane_funcs intel_plane_funcs = { .update_plane = intel_update_plane, .disable_plane = intel_disable_plane, .destroy = intel_destroy_plane, + .set_property = intel_plane_set_property, }; static uint32_t ilk_plane_formats[] = { @@ -1310,13 +1376,28 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane) intel_plane->pipe = pipe; intel_plane->plane = plane; + intel_plane->rotation = BIT(DRM_ROTATE_0); possible_crtcs = (1 << pipe); - ret = drm_plane_init(dev, &intel_plane->base, possible_crtcs, - &intel_plane_funcs, - plane_formats, num_plane_formats, - false); - if (ret) + ret = drm_universal_plane_init(dev, &intel_plane->base, possible_crtcs, + &intel_plane_funcs, + plane_formats, num_plane_formats, + DRM_PLANE_TYPE_OVERLAY); + if (ret) { kfree(intel_plane); + goto out; + } + + if (!dev->mode_config.rotation_property) + dev->mode_config.rotation_property = + drm_mode_create_rotation_property(dev, + BIT(DRM_ROTATE_0) | + BIT(DRM_ROTATE_180)); + + if (dev->mode_config.rotation_property) + drm_object_attach_property(&intel_plane->base.base, + dev->mode_config.rotation_property, + intel_plane->rotation); + out: return ret; } diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index e81bc3bdc533b5ae60a7fae77536d1dd83caf1da..918b761639651042edebf1ab7a3d886973774a15 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -101,7 +101,7 @@ static void __gen7_gt_force_wake_mt_get(struct drm_i915_private *dev_priv, { u32 forcewake_ack; - if (IS_HASWELL(dev_priv->dev) || IS_GEN8(dev_priv->dev)) + if (IS_HASWELL(dev_priv->dev) || IS_BROADWELL(dev_priv->dev)) forcewake_ack = FORCEWAKE_ACK_HSW; else forcewake_ack = FORCEWAKE_MT_ACK; @@ -334,7 +334,7 @@ void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore) else if (IS_GEN6(dev) || IS_GEN7(dev)) __gen6_gt_force_wake_reset(dev_priv); - if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_GEN8(dev)) + if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_BROADWELL(dev)) __gen7_gt_force_wake_mt_reset(dev_priv); if (restore) { /* If reset with a user forcewake, try to restore */ @@ -838,7 +838,7 @@ void intel_uncore_init(struct drm_device *dev) if (IS_VALLEYVIEW(dev)) { dev_priv->uncore.funcs.force_wake_get = __vlv_force_wake_get; dev_priv->uncore.funcs.force_wake_put = __vlv_force_wake_put; - } else if (IS_HASWELL(dev) || IS_GEN8(dev)) { + } else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) { dev_priv->uncore.funcs.force_wake_get = __gen7_gt_force_wake_mt_get; dev_priv->uncore.funcs.force_wake_put = __gen7_gt_force_wake_mt_put; } else if (IS_IVYBRIDGE(dev)) { diff --git a/drivers/gpu/drm/mga/mga_dma.c b/drivers/gpu/drm/mga/mga_dma.c index c3bf059ba720569540145f225222e0f2b444827c..8cfa9cb74c8679a329ff14293f0e1fd4b2927323 100644 --- a/drivers/gpu/drm/mga/mga_dma.c +++ b/drivers/gpu/drm/mga/mga_dma.c @@ -502,31 +502,31 @@ static int mga_do_agp_dma_bootstrap(struct drm_device *dev, return err; } - /* Make drm_addbufs happy by not trying to create a mapping for less - * than a page. + /* Make drm_legacy_addbufs happy by not trying to create a mapping for + * less than a page. */ if (warp_size < PAGE_SIZE) warp_size = PAGE_SIZE; offset = 0; - err = drm_addmap(dev, offset, warp_size, - _DRM_AGP, _DRM_READ_ONLY, &dev_priv->warp); + err = drm_legacy_addmap(dev, offset, warp_size, + _DRM_AGP, _DRM_READ_ONLY, &dev_priv->warp); if (err) { DRM_ERROR("Unable to map WARP microcode: %d\n", err); return err; } offset += warp_size; - err = drm_addmap(dev, offset, dma_bs->primary_size, - _DRM_AGP, _DRM_READ_ONLY, &dev_priv->primary); + err = drm_legacy_addmap(dev, offset, dma_bs->primary_size, + _DRM_AGP, _DRM_READ_ONLY, &dev_priv->primary); if (err) { DRM_ERROR("Unable to map primary DMA region: %d\n", err); return err; } offset += dma_bs->primary_size; - err = drm_addmap(dev, offset, secondary_size, - _DRM_AGP, 0, &dev->agp_buffer_map); + err = drm_legacy_addmap(dev, offset, secondary_size, + _DRM_AGP, 0, &dev->agp_buffer_map); if (err) { DRM_ERROR("Unable to map secondary DMA region: %d\n", err); return err; @@ -538,7 +538,7 @@ static int mga_do_agp_dma_bootstrap(struct drm_device *dev, req.flags = _DRM_AGP_BUFFER; req.agp_start = offset; - err = drm_addbufs_agp(dev, &req); + err = drm_legacy_addbufs_agp(dev, &req); if (err) { DRM_ERROR("Unable to add secondary DMA buffers: %d\n", err); return err; @@ -559,16 +559,16 @@ static int mga_do_agp_dma_bootstrap(struct drm_device *dev, } offset += secondary_size; - err = drm_addmap(dev, offset, agp_size - offset, - _DRM_AGP, 0, &dev_priv->agp_textures); + err = drm_legacy_addmap(dev, offset, agp_size - offset, + _DRM_AGP, 0, &dev_priv->agp_textures); if (err) { DRM_ERROR("Unable to map AGP texture region %d\n", err); return err; } - drm_core_ioremap(dev_priv->warp, dev); - drm_core_ioremap(dev_priv->primary, dev); - drm_core_ioremap(dev->agp_buffer_map, dev); + drm_legacy_ioremap(dev_priv->warp, dev); + drm_legacy_ioremap(dev_priv->primary, dev); + drm_legacy_ioremap(dev->agp_buffer_map, dev); if (!dev_priv->warp->handle || !dev_priv->primary->handle || !dev->agp_buffer_map->handle) { @@ -602,7 +602,7 @@ static int mga_do_agp_dma_bootstrap(struct drm_device *dev, * * \todo * Determine whether the maximum address passed to drm_pci_alloc is correct. - * The same goes for drm_addbufs_pci. + * The same goes for drm_legacy_addbufs_pci. * * \sa mga_do_dma_bootstrap, mga_do_agp_dma_bootstrap */ @@ -622,15 +622,15 @@ static int mga_do_pci_dma_bootstrap(struct drm_device *dev, return -EFAULT; } - /* Make drm_addbufs happy by not trying to create a mapping for less - * than a page. + /* Make drm_legacy_addbufs happy by not trying to create a mapping for + * less than a page. */ if (warp_size < PAGE_SIZE) warp_size = PAGE_SIZE; /* The proper alignment is 0x100 for this mapping */ - err = drm_addmap(dev, 0, warp_size, _DRM_CONSISTENT, - _DRM_READ_ONLY, &dev_priv->warp); + err = drm_legacy_addmap(dev, 0, warp_size, _DRM_CONSISTENT, + _DRM_READ_ONLY, &dev_priv->warp); if (err != 0) { DRM_ERROR("Unable to create mapping for WARP microcode: %d\n", err); @@ -645,8 +645,8 @@ static int mga_do_pci_dma_bootstrap(struct drm_device *dev, for (primary_size = dma_bs->primary_size; primary_size != 0; primary_size >>= 1) { /* The proper alignment for this mapping is 0x04 */ - err = drm_addmap(dev, 0, primary_size, _DRM_CONSISTENT, - _DRM_READ_ONLY, &dev_priv->primary); + err = drm_legacy_addmap(dev, 0, primary_size, _DRM_CONSISTENT, + _DRM_READ_ONLY, &dev_priv->primary); if (!err) break; } @@ -669,7 +669,7 @@ static int mga_do_pci_dma_bootstrap(struct drm_device *dev, req.count = bin_count; req.size = dma_bs->secondary_bin_size; - err = drm_addbufs_pci(dev, &req); + err = drm_legacy_addbufs_pci(dev, &req); if (!err) break; } @@ -708,15 +708,16 @@ static int mga_do_dma_bootstrap(struct drm_device *dev, /* The first steps are the same for both PCI and AGP based DMA. Map * the cards MMIO registers and map a status page. */ - err = drm_addmap(dev, dev_priv->mmio_base, dev_priv->mmio_size, - _DRM_REGISTERS, _DRM_READ_ONLY, &dev_priv->mmio); + err = drm_legacy_addmap(dev, dev_priv->mmio_base, dev_priv->mmio_size, + _DRM_REGISTERS, _DRM_READ_ONLY, + &dev_priv->mmio); if (err) { DRM_ERROR("Unable to map MMIO region: %d\n", err); return err; } - err = drm_addmap(dev, 0, SAREA_MAX, _DRM_SHM, - _DRM_READ_ONLY | _DRM_LOCKED | _DRM_KERNEL, + err = drm_legacy_addmap(dev, 0, SAREA_MAX, _DRM_SHM, + _DRM_READ_ONLY | _DRM_LOCKED | _DRM_KERNEL, &dev_priv->status); if (err) { DRM_ERROR("Unable to map status region: %d\n", err); @@ -809,7 +810,7 @@ static int mga_do_init_dma(struct drm_device *dev, drm_mga_init_t *init) dev_priv->texture_offset = init->texture_offset[0]; dev_priv->texture_size = init->texture_size[0]; - dev_priv->sarea = drm_getsarea(dev); + dev_priv->sarea = drm_legacy_getsarea(dev); if (!dev_priv->sarea) { DRM_ERROR("failed to find sarea!\n"); return -EINVAL; @@ -820,37 +821,37 @@ static int mga_do_init_dma(struct drm_device *dev, drm_mga_init_t *init) dev_priv->dma_access = MGA_PAGPXFER; dev_priv->wagp_enable = MGA_WAGP_ENABLE; - dev_priv->status = drm_core_findmap(dev, init->status_offset); + dev_priv->status = drm_legacy_findmap(dev, init->status_offset); if (!dev_priv->status) { DRM_ERROR("failed to find status page!\n"); return -EINVAL; } - dev_priv->mmio = drm_core_findmap(dev, init->mmio_offset); + dev_priv->mmio = drm_legacy_findmap(dev, init->mmio_offset); if (!dev_priv->mmio) { DRM_ERROR("failed to find mmio region!\n"); return -EINVAL; } - dev_priv->warp = drm_core_findmap(dev, init->warp_offset); + dev_priv->warp = drm_legacy_findmap(dev, init->warp_offset); if (!dev_priv->warp) { DRM_ERROR("failed to find warp microcode region!\n"); return -EINVAL; } - dev_priv->primary = drm_core_findmap(dev, init->primary_offset); + dev_priv->primary = drm_legacy_findmap(dev, init->primary_offset); if (!dev_priv->primary) { DRM_ERROR("failed to find primary dma region!\n"); return -EINVAL; } dev->agp_buffer_token = init->buffers_offset; dev->agp_buffer_map = - drm_core_findmap(dev, init->buffers_offset); + drm_legacy_findmap(dev, init->buffers_offset); if (!dev->agp_buffer_map) { DRM_ERROR("failed to find dma buffer region!\n"); return -EINVAL; } - drm_core_ioremap(dev_priv->warp, dev); - drm_core_ioremap(dev_priv->primary, dev); - drm_core_ioremap(dev->agp_buffer_map, dev); + drm_legacy_ioremap(dev_priv->warp, dev); + drm_legacy_ioremap(dev_priv->primary, dev); + drm_legacy_ioremap(dev->agp_buffer_map, dev); } dev_priv->sarea_priv = @@ -936,14 +937,14 @@ static int mga_do_cleanup_dma(struct drm_device *dev, int full_cleanup) if ((dev_priv->warp != NULL) && (dev_priv->warp->type != _DRM_CONSISTENT)) - drm_core_ioremapfree(dev_priv->warp, dev); + drm_legacy_ioremapfree(dev_priv->warp, dev); if ((dev_priv->primary != NULL) && (dev_priv->primary->type != _DRM_CONSISTENT)) - drm_core_ioremapfree(dev_priv->primary, dev); + drm_legacy_ioremapfree(dev_priv->primary, dev); if (dev->agp_buffer_map != NULL) - drm_core_ioremapfree(dev->agp_buffer_map, dev); + drm_legacy_ioremapfree(dev->agp_buffer_map, dev); if (dev_priv->used_new_dma_init) { #if __OS_HAS_AGP diff --git a/drivers/gpu/drm/mga/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c index 6b1a87c8aac52f6f103385a7302713e719a3a1b3..5e2f131a6a72943f4f911c7428d507adae735bfa 100644 --- a/drivers/gpu/drm/mga/mga_drv.c +++ b/drivers/gpu/drm/mga/mga_drv.c @@ -48,7 +48,7 @@ static const struct file_operations mga_driver_fops = { .open = drm_open, .release = drm_release, .unlocked_ioctl = drm_ioctl, - .mmap = drm_mmap, + .mmap = drm_legacy_mmap, .poll = drm_poll, #ifdef CONFIG_COMPAT .compat_ioctl = mga_compat_ioctl, @@ -64,6 +64,7 @@ static struct drm_driver driver = { .load = mga_driver_load, .unload = mga_driver_unload, .lastclose = mga_driver_lastclose, + .set_busid = drm_pci_set_busid, .dma_quiescent = mga_driver_dma_quiescent, .device_is_agp = mga_driver_device_is_agp, .get_vblank_counter = mga_get_vblank_counter, diff --git a/drivers/gpu/drm/mga/mga_drv.h b/drivers/gpu/drm/mga/mga_drv.h index fe453213600ab728e57385e40b272e4e8b8ebc49..b4a2014917e525109eaccdd5cd2237e6eb82915d 100644 --- a/drivers/gpu/drm/mga/mga_drv.h +++ b/drivers/gpu/drm/mga/mga_drv.h @@ -31,6 +31,8 @@ #ifndef __MGA_DRV_H__ #define __MGA_DRV_H__ +#include + /* General customization: */ diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 2d75d6df0789d5fd7d0515995ae1f80a35e982ca..97745991544ddd4a02000ed506a0a9cce5ccf8b1 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -91,6 +91,7 @@ static struct drm_driver driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET, .load = mgag200_driver_load, .unload = mgag200_driver_unload, + .set_busid = drm_pci_set_busid, .fops = &mgag200_driver_fops, .name = DRIVER_NAME, .desc = DRIVER_DESC, diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h index 80de23d9b9c9801daf630611205a2baa768a3341..e9eea1d4e7c3bff677d3b947d959685c7aa210cf 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.h +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -22,6 +22,8 @@ #include #include +#include + #include #include @@ -190,8 +192,6 @@ struct mga_device { resource_size_t rmmio_size; void __iomem *rmmio; - drm_local_map_t *framebuffer; - struct mga_mc mc; struct mga_mode_info mode_info; @@ -224,7 +224,7 @@ struct mgag200_bo { struct ttm_placement placement; struct ttm_bo_kmap_obj kmap; struct drm_gem_object gem; - u32 placements[3]; + struct ttm_place placements[3]; int pin_count; }; #define gem_to_mga_bo(gobj) container_of((gobj), struct mgag200_bo, gem) diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 5451dc58eff19ccb81d2eb4780c464e314ae6b11..4415af3666ab1116e0a07de45ffe1e272f5f4a9a 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -158,7 +158,8 @@ static int mgag200fb_create_object(struct mga_fbdev *afbdev, static int mgag200fb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { - struct mga_fbdev *mfbdev = (struct mga_fbdev *)helper; + struct mga_fbdev *mfbdev = + container_of(helper, struct mga_fbdev, helper); struct drm_device *dev = mfbdev->helper.dev; struct drm_mode_fb_cmd2 mode_cmd; struct mga_device *mdev = dev->dev_private; diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index 45f04dea0ac2ad9f3f37847251e15b4fb0d09e80..83485ab81ce8714214c258b537fd1609e12bae2a 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -1483,11 +1483,7 @@ static int mga_vga_mode_valid(struct drm_connector *connector, { struct drm_device *dev = connector->dev; struct mga_device *mdev = (struct mga_device*)dev->dev_private; - struct mga_fbdev *mfbdev = mdev->mfbdev; - struct drm_fb_helper *fb_helper = &mfbdev->helper; - struct drm_fb_helper_connector *fb_helper_conn = NULL; int bpp = 32; - int i = 0; if (IS_G200_SE(mdev)) { if (mdev->unique_rev_id == 0x01) { @@ -1537,21 +1533,14 @@ static int mga_vga_mode_valid(struct drm_connector *connector, } /* Validate the mode input by the user */ - for (i = 0; i < fb_helper->connector_count; i++) { - if (fb_helper->connector_info[i]->connector == connector) { - /* Found the helper for this connector */ - fb_helper_conn = fb_helper->connector_info[i]; - if (fb_helper_conn->cmdline_mode.specified) { - if (fb_helper_conn->cmdline_mode.bpp_specified) { - bpp = fb_helper_conn->cmdline_mode.bpp; - } - } - } + if (connector->cmdline_mode.specified) { + if (connector->cmdline_mode.bpp_specified) + bpp = connector->cmdline_mode.bpp; } if ((mode->hdisplay * mode->vdisplay * (bpp/8)) > mdev->mc.vram_size) { - if (fb_helper_conn) - fb_helper_conn->cmdline_mode.specified = false; + if (connector->cmdline_mode.specified) + connector->cmdline_mode.specified = false; return MODE_BAD; } diff --git a/drivers/gpu/drm/mgag200/mgag200_ttm.c b/drivers/gpu/drm/mgag200/mgag200_ttm.c index 5a00e90696de525129a98d9e4a438ab7b3779a49..d16964ea0ed4820039ca1b90030032e8ed735212 100644 --- a/drivers/gpu/drm/mgag200/mgag200_ttm.c +++ b/drivers/gpu/drm/mgag200/mgag200_ttm.c @@ -293,18 +293,22 @@ void mgag200_mm_fini(struct mga_device *mdev) void mgag200_ttm_placement(struct mgag200_bo *bo, int domain) { u32 c = 0; - bo->placement.fpfn = 0; - bo->placement.lpfn = 0; + unsigned i; + bo->placement.placement = bo->placements; bo->placement.busy_placement = bo->placements; if (domain & TTM_PL_FLAG_VRAM) - bo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM; + bo->placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM; if (domain & TTM_PL_FLAG_SYSTEM) - bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + bo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; if (!c) - bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + bo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; bo->placement.num_placement = c; bo->placement.num_busy_placement = c; + for (i = 0; i < c; ++i) { + bo->placements[i].fpfn = 0; + bo->placements[i].lpfn = 0; + } } int mgag200_bo_create(struct drm_device *dev, int size, int align, @@ -335,7 +339,7 @@ int mgag200_bo_create(struct drm_device *dev, int size, int align, ret = ttm_bo_init(&mdev->ttm.bdev, &mgabo->bo, size, ttm_bo_type_device, &mgabo->placement, align >> PAGE_SHIFT, false, NULL, acc_size, - NULL, mgag200_bo_ttm_destroy); + NULL, NULL, mgag200_bo_ttm_destroy); if (ret) return ret; @@ -361,7 +365,7 @@ int mgag200_bo_pin(struct mgag200_bo *bo, u32 pl_flag, u64 *gpu_addr) mgag200_ttm_placement(bo, pl_flag); for (i = 0; i < bo->placement.num_placement; i++) - bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) return ret; @@ -384,7 +388,7 @@ int mgag200_bo_unpin(struct mgag200_bo *bo) return 0; for (i = 0; i < bo->placement.num_placement ; i++) - bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT; + bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) return ret; @@ -408,7 +412,7 @@ int mgag200_bo_push_sysram(struct mgag200_bo *bo) mgag200_ttm_placement(bo, TTM_PL_FLAG_SYSTEM); for (i = 0; i < bo->placement.num_placement ; i++) - bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) { @@ -424,7 +428,7 @@ int mgag200_mmap(struct file *filp, struct vm_area_struct *vma) struct mga_device *mdev; if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) - return drm_mmap(filp, vma); + return -EINVAL; file_priv = filp->private_data; mdev = file_priv->minor->dev->dev_private; diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index c99c50de3226d663bea299b722faeb2691581fbe..9d907c526c94d4df4d9a70a527678c2d58a743ff 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -4,6 +4,7 @@ config DRM_MSM depends on DRM depends on ARCH_QCOM || (ARM && COMPILE_TEST) select DRM_KMS_HELPER + select DRM_PANEL select SHMEM select TMPFS default y diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 93ca49c8df44b06cfe832bfdf03eeccd5d559bb0..6283dcb96af51f8bd5e70c26a507412f216996f6 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -4,6 +4,7 @@ ifeq (, $(findstring -W,$(EXTRA_CFLAGS))) endif msm-y := \ + adreno/adreno_device.o \ adreno/adreno_gpu.o \ adreno/a3xx_gpu.o \ hdmi/hdmi.o \ @@ -18,6 +19,8 @@ msm-y := \ mdp/mdp_kms.o \ mdp/mdp4/mdp4_crtc.o \ mdp/mdp4/mdp4_dtv_encoder.o \ + mdp/mdp4/mdp4_lcdc_encoder.o \ + mdp/mdp4/mdp4_lvds_connector.o \ mdp/mdp4/mdp4_irq.o \ mdp/mdp4/mdp4_kms.o \ mdp/mdp4/mdp4_plane.o \ @@ -39,5 +42,6 @@ msm-y := \ msm_ringbuffer.o msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o +msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o obj-$(CONFIG_DRM_MSM) += msm.o diff --git a/drivers/gpu/drm/msm/adreno/a2xx.xml.h b/drivers/gpu/drm/msm/adreno/a2xx.xml.h index a8a144b38eaabe12329f46f5ef9eea258c8f0c4b..a3104598c27f8117b5917ab2bdf7c018ad514383 100644 --- a/drivers/gpu/drm/msm/adreno/a2xx.xml.h +++ b/drivers/gpu/drm/msm/adreno/a2xx.xml.h @@ -12,9 +12,9 @@ The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) - /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2014-06-02 15:21:30) - /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 9859 bytes, from 2014-06-02 15:21:30) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14477 bytes, from 2014-05-16 11:51:57) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 58020 bytes, from 2014-06-25 12:57:16) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 26602 bytes, from 2014-06-25 12:57:16) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14960 bytes, from 2014-07-27 17:22:13) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 58020 bytes, from 2014-08-01 12:22:48) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 41068 bytes, from 2014-08-01 12:22:48) Copyright (C) 2013-2014 by the following authors: - Rob Clark (robclark) diff --git a/drivers/gpu/drm/msm/adreno/a3xx.xml.h b/drivers/gpu/drm/msm/adreno/a3xx.xml.h index 303e8a9e91a595bf5a09075d1865319c8206cada..82d015279b47bf493fc0b5773c2306387d17c7ce 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx.xml.h +++ b/drivers/gpu/drm/msm/adreno/a3xx.xml.h @@ -12,9 +12,9 @@ The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) - /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2014-06-02 15:21:30) - /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 9859 bytes, from 2014-06-02 15:21:30) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14477 bytes, from 2014-05-16 11:51:57) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 58020 bytes, from 2014-06-25 12:57:16) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 26602 bytes, from 2014-06-25 12:57:16) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14960 bytes, from 2014-07-27 17:22:13) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 58020 bytes, from 2014-08-01 12:22:48) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 41068 bytes, from 2014-08-01 12:22:48) Copyright (C) 2013-2014 by the following authors: - Rob Clark (robclark) @@ -654,7 +654,7 @@ static inline uint32_t A3XX_GRAS_SU_POINT_SIZE(float val) #define A3XX_GRAS_SU_POLY_OFFSET_SCALE_VAL__SHIFT 0 static inline uint32_t A3XX_GRAS_SU_POLY_OFFSET_SCALE_VAL(float val) { - return ((((uint32_t)(val * 40.0))) << A3XX_GRAS_SU_POLY_OFFSET_SCALE_VAL__SHIFT) & A3XX_GRAS_SU_POLY_OFFSET_SCALE_VAL__MASK; + return ((((uint32_t)(val * 28.0))) << A3XX_GRAS_SU_POLY_OFFSET_SCALE_VAL__SHIFT) & A3XX_GRAS_SU_POLY_OFFSET_SCALE_VAL__MASK; } #define REG_A3XX_GRAS_SU_POLY_OFFSET_OFFSET 0x0000206d @@ -662,7 +662,7 @@ static inline uint32_t A3XX_GRAS_SU_POLY_OFFSET_SCALE_VAL(float val) #define A3XX_GRAS_SU_POLY_OFFSET_OFFSET__SHIFT 0 static inline uint32_t A3XX_GRAS_SU_POLY_OFFSET_OFFSET(float val) { - return ((((uint32_t)(val * 44.0))) << A3XX_GRAS_SU_POLY_OFFSET_OFFSET__SHIFT) & A3XX_GRAS_SU_POLY_OFFSET_OFFSET__MASK; + return ((((uint32_t)(val * 28.0))) << A3XX_GRAS_SU_POLY_OFFSET_OFFSET__SHIFT) & A3XX_GRAS_SU_POLY_OFFSET_OFFSET__MASK; } #define REG_A3XX_GRAS_SU_MODE_CONTROL 0x00002070 @@ -1696,7 +1696,7 @@ static inline uint32_t A3XX_SP_VS_CTRL_REG1_CONSTFOOTPRINT(uint32_t val) { return ((val) << A3XX_SP_VS_CTRL_REG1_CONSTFOOTPRINT__SHIFT) & A3XX_SP_VS_CTRL_REG1_CONSTFOOTPRINT__MASK; } -#define A3XX_SP_VS_CTRL_REG1_INITIALOUTSTANDING__MASK 0x3f000000 +#define A3XX_SP_VS_CTRL_REG1_INITIALOUTSTANDING__MASK 0x7f000000 #define A3XX_SP_VS_CTRL_REG1_INITIALOUTSTANDING__SHIFT 24 static inline uint32_t A3XX_SP_VS_CTRL_REG1_INITIALOUTSTANDING(uint32_t val) { diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index 2773600c94888c180c76100dd1848ad395bc596a..218c5b0603989b3474ef7815b91d6ec0371ab013 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -35,10 +35,8 @@ A3XX_INT0_CP_AHB_ERROR_HALT | \ A3XX_INT0_UCHE_OOB_ACCESS) +extern bool hang_debug; -static bool hang_debug = false; -MODULE_PARM_DESC(hang_debug, "Dump registers when hang is detected (can be slow!)"); -module_param_named(hang_debug, hang_debug, bool, 0600); static void a3xx_dump(struct msm_gpu *gpu); static void a3xx_me_init(struct msm_gpu *gpu) @@ -387,58 +385,26 @@ static const unsigned int a3xx_registers[] = { 0x2750, 0x2756, 0x2760, 0x2760, 0x300c, 0x300e, 0x301c, 0x301d, 0x302a, 0x302a, 0x302c, 0x302d, 0x3030, 0x3031, 0x3034, 0x3036, 0x303c, 0x303c, 0x305e, 0x305f, + ~0 /* sentinel */ }; #ifdef CONFIG_DEBUG_FS static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m) { - int i; - - adreno_show(gpu, m); - gpu->funcs->pm_resume(gpu); - seq_printf(m, "status: %08x\n", gpu_read(gpu, REG_A3XX_RBBM_STATUS)); - - /* dump these out in a form that can be parsed by demsm: */ - seq_printf(m, "IO:region %s 00000000 00020000\n", gpu->name); - for (i = 0; i < ARRAY_SIZE(a3xx_registers); i += 2) { - uint32_t start = a3xx_registers[i]; - uint32_t end = a3xx_registers[i+1]; - uint32_t addr; - - for (addr = start; addr <= end; addr++) { - uint32_t val = gpu_read(gpu, addr); - seq_printf(m, "IO:R %08x %08x\n", addr<<2, val); - } - } - gpu->funcs->pm_suspend(gpu); + adreno_show(gpu, m); } #endif /* would be nice to not have to duplicate the _show() stuff with printk(): */ static void a3xx_dump(struct msm_gpu *gpu) { - int i; - - adreno_dump(gpu); printk("status: %08x\n", gpu_read(gpu, REG_A3XX_RBBM_STATUS)); - - /* dump these out in a form that can be parsed by demsm: */ - printk("IO:region %s 00000000 00020000\n", gpu->name); - for (i = 0; i < ARRAY_SIZE(a3xx_registers); i += 2) { - uint32_t start = a3xx_registers[i]; - uint32_t end = a3xx_registers[i+1]; - uint32_t addr; - - for (addr = start; addr <= end; addr++) { - uint32_t val = gpu_read(gpu, addr); - printk("IO:R %08x %08x\n", addr<<2, val); - } - } + adreno_dump(gpu); } static const struct adreno_gpu_funcs funcs = { @@ -474,7 +440,6 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev) struct msm_gpu *gpu; struct msm_drm_private *priv = dev->dev_private; struct platform_device *pdev = priv->gpu_pdev; - struct adreno_platform_config *config; int ret; if (!pdev) { @@ -483,8 +448,6 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev) goto fail; } - config = pdev->dev.platform_data; - a3xx_gpu = kzalloc(sizeof(*a3xx_gpu), GFP_KERNEL); if (!a3xx_gpu) { ret = -ENOMEM; @@ -496,20 +459,12 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev) a3xx_gpu->pdev = pdev; - gpu->fast_rate = config->fast_rate; - gpu->slow_rate = config->slow_rate; - gpu->bus_freq = config->bus_freq; -#ifdef CONFIG_MSM_BUS_SCALING - gpu->bus_scale_table = config->bus_scale_table; -#endif - - DBG("fast_rate=%u, slow_rate=%u, bus_freq=%u", - gpu->fast_rate, gpu->slow_rate, gpu->bus_freq); - gpu->perfcntrs = perfcntrs; gpu->num_perfcntrs = ARRAY_SIZE(perfcntrs); - ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, config->rev); + adreno_gpu->registers = a3xx_registers; + + ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs); if (ret) goto fail; @@ -549,158 +504,3 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev) return ERR_PTR(ret); } - -/* - * The a3xx device: - */ - -#if defined(CONFIG_MSM_BUS_SCALING) && !defined(CONFIG_OF) -# include -#endif - -static void set_gpu_pdev(struct drm_device *dev, - struct platform_device *pdev) -{ - struct msm_drm_private *priv = dev->dev_private; - priv->gpu_pdev = pdev; -} - -static int a3xx_bind(struct device *dev, struct device *master, void *data) -{ - static struct adreno_platform_config config = {}; -#ifdef CONFIG_OF - struct device_node *child, *node = dev->of_node; - u32 val; - int ret; - - ret = of_property_read_u32(node, "qcom,chipid", &val); - if (ret) { - dev_err(dev, "could not find chipid: %d\n", ret); - return ret; - } - - config.rev = ADRENO_REV((val >> 24) & 0xff, - (val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff); - - /* find clock rates: */ - config.fast_rate = 0; - config.slow_rate = ~0; - for_each_child_of_node(node, child) { - if (of_device_is_compatible(child, "qcom,gpu-pwrlevels")) { - struct device_node *pwrlvl; - for_each_child_of_node(child, pwrlvl) { - ret = of_property_read_u32(pwrlvl, "qcom,gpu-freq", &val); - if (ret) { - dev_err(dev, "could not find gpu-freq: %d\n", ret); - return ret; - } - config.fast_rate = max(config.fast_rate, val); - config.slow_rate = min(config.slow_rate, val); - } - } - } - - if (!config.fast_rate) { - dev_err(dev, "could not find clk rates\n"); - return -ENXIO; - } - -#else - struct kgsl_device_platform_data *pdata = dev->platform_data; - uint32_t version = socinfo_get_version(); - if (cpu_is_apq8064ab()) { - config.fast_rate = 450000000; - config.slow_rate = 27000000; - config.bus_freq = 4; - config.rev = ADRENO_REV(3, 2, 1, 0); - } else if (cpu_is_apq8064()) { - config.fast_rate = 400000000; - config.slow_rate = 27000000; - config.bus_freq = 4; - - if (SOCINFO_VERSION_MAJOR(version) == 2) - config.rev = ADRENO_REV(3, 2, 0, 2); - else if ((SOCINFO_VERSION_MAJOR(version) == 1) && - (SOCINFO_VERSION_MINOR(version) == 1)) - config.rev = ADRENO_REV(3, 2, 0, 1); - else - config.rev = ADRENO_REV(3, 2, 0, 0); - - } else if (cpu_is_msm8960ab()) { - config.fast_rate = 400000000; - config.slow_rate = 320000000; - config.bus_freq = 4; - - if (SOCINFO_VERSION_MINOR(version) == 0) - config.rev = ADRENO_REV(3, 2, 1, 0); - else - config.rev = ADRENO_REV(3, 2, 1, 1); - - } else if (cpu_is_msm8930()) { - config.fast_rate = 400000000; - config.slow_rate = 27000000; - config.bus_freq = 3; - - if ((SOCINFO_VERSION_MAJOR(version) == 1) && - (SOCINFO_VERSION_MINOR(version) == 2)) - config.rev = ADRENO_REV(3, 0, 5, 2); - else - config.rev = ADRENO_REV(3, 0, 5, 0); - - } -# ifdef CONFIG_MSM_BUS_SCALING - config.bus_scale_table = pdata->bus_scale_table; -# endif -#endif - dev->platform_data = &config; - set_gpu_pdev(dev_get_drvdata(master), to_platform_device(dev)); - return 0; -} - -static void a3xx_unbind(struct device *dev, struct device *master, - void *data) -{ - set_gpu_pdev(dev_get_drvdata(master), NULL); -} - -static const struct component_ops a3xx_ops = { - .bind = a3xx_bind, - .unbind = a3xx_unbind, -}; - -static int a3xx_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &a3xx_ops); -} - -static int a3xx_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &a3xx_ops); - return 0; -} - -static const struct of_device_id dt_match[] = { - { .compatible = "qcom,adreno-3xx" }, - /* for backwards compat w/ downstream kgsl DT files: */ - { .compatible = "qcom,kgsl-3d0" }, - {} -}; - -static struct platform_driver a3xx_driver = { - .probe = a3xx_probe, - .remove = a3xx_remove, - .driver = { - .name = "kgsl-3d0", - .of_match_table = dt_match, - }, -}; - -void __init a3xx_register(void) -{ - platform_driver_register(&a3xx_driver); -} - -void __exit a3xx_unregister(void) -{ - platform_driver_unregister(&a3xx_driver); -} diff --git a/drivers/gpu/drm/msm/adreno/adreno_common.xml.h b/drivers/gpu/drm/msm/adreno/adreno_common.xml.h index 9de19ac2e86c7d7e310a4f5e55df37cdcb1d26a3..cc341bc62b51f8ff860f85e11663c6d6d2f2ff81 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_common.xml.h +++ b/drivers/gpu/drm/msm/adreno/adreno_common.xml.h @@ -12,9 +12,9 @@ The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) - /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2014-06-02 15:21:30) - /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 9859 bytes, from 2014-06-02 15:21:30) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14477 bytes, from 2014-05-16 11:51:57) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 58020 bytes, from 2014-06-25 12:57:16) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 26602 bytes, from 2014-06-25 12:57:16) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14960 bytes, from 2014-07-27 17:22:13) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 58020 bytes, from 2014-08-01 12:22:48) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 41068 bytes, from 2014-08-01 12:22:48) Copyright (C) 2013-2014 by the following authors: - Rob Clark (robclark) diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c new file mode 100644 index 0000000000000000000000000000000000000000..7ab85af3a7dbcc8b83299229bbbeac39ca99e05f --- /dev/null +++ b/drivers/gpu/drm/msm/adreno/adreno_device.c @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2013-2014 Red Hat + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "adreno_gpu.h" + +#if defined(CONFIG_MSM_BUS_SCALING) && !defined(CONFIG_OF) +# include +#endif + +#define ANY_ID 0xff + +bool hang_debug = false; +MODULE_PARM_DESC(hang_debug, "Dump registers when hang is detected (can be slow!)"); +module_param_named(hang_debug, hang_debug, bool, 0600); + +struct msm_gpu *a3xx_gpu_init(struct drm_device *dev); + +static const struct adreno_info gpulist[] = { + { + .rev = ADRENO_REV(3, 0, 5, ANY_ID), + .revn = 305, + .name = "A305", + .pm4fw = "a300_pm4.fw", + .pfpfw = "a300_pfp.fw", + .gmem = SZ_256K, + .init = a3xx_gpu_init, + }, { + .rev = ADRENO_REV(3, 2, ANY_ID, ANY_ID), + .revn = 320, + .name = "A320", + .pm4fw = "a300_pm4.fw", + .pfpfw = "a300_pfp.fw", + .gmem = SZ_512K, + .init = a3xx_gpu_init, + }, { + .rev = ADRENO_REV(3, 3, 0, ANY_ID), + .revn = 330, + .name = "A330", + .pm4fw = "a330_pm4.fw", + .pfpfw = "a330_pfp.fw", + .gmem = SZ_1M, + .init = a3xx_gpu_init, + }, +}; + +MODULE_FIRMWARE("a300_pm4.fw"); +MODULE_FIRMWARE("a300_pfp.fw"); +MODULE_FIRMWARE("a330_pm4.fw"); +MODULE_FIRMWARE("a330_pfp.fw"); + +static inline bool _rev_match(uint8_t entry, uint8_t id) +{ + return (entry == ANY_ID) || (entry == id); +} + +const struct adreno_info *adreno_info(struct adreno_rev rev) +{ + int i; + + /* identify gpu: */ + for (i = 0; i < ARRAY_SIZE(gpulist); i++) { + const struct adreno_info *info = &gpulist[i]; + if (_rev_match(info->rev.core, rev.core) && + _rev_match(info->rev.major, rev.major) && + _rev_match(info->rev.minor, rev.minor) && + _rev_match(info->rev.patchid, rev.patchid)) + return info; + } + + return NULL; +} + +struct msm_gpu *adreno_load_gpu(struct drm_device *dev) +{ + struct msm_drm_private *priv = dev->dev_private; + struct platform_device *pdev = priv->gpu_pdev; + struct adreno_platform_config *config; + struct adreno_rev rev; + const struct adreno_info *info; + struct msm_gpu *gpu = NULL; + + if (!pdev) { + dev_err(dev->dev, "no adreno device\n"); + return NULL; + } + + config = pdev->dev.platform_data; + rev = config->rev; + info = adreno_info(config->rev); + + if (!info) { + dev_warn(dev->dev, "Unknown GPU revision: %u.%u.%u.%u\n", + rev.core, rev.major, rev.minor, rev.patchid); + return NULL; + } + + DBG("Found GPU: %u.%u.%u.%u", rev.core, rev.major, + rev.minor, rev.patchid); + + gpu = info->init(dev); + if (IS_ERR(gpu)) { + dev_warn(dev->dev, "failed to load adreno gpu\n"); + gpu = NULL; + /* not fatal */ + } + + if (gpu) { + int ret; + mutex_lock(&dev->struct_mutex); + gpu->funcs->pm_resume(gpu); + mutex_unlock(&dev->struct_mutex); + ret = gpu->funcs->hw_init(gpu); + if (ret) { + dev_err(dev->dev, "gpu hw init failed: %d\n", ret); + gpu->funcs->destroy(gpu); + gpu = NULL; + } else { + /* give inactive pm a chance to kick in: */ + msm_gpu_retire(gpu); + } + } + + return gpu; +} + +static void set_gpu_pdev(struct drm_device *dev, + struct platform_device *pdev) +{ + struct msm_drm_private *priv = dev->dev_private; + priv->gpu_pdev = pdev; +} + +static int adreno_bind(struct device *dev, struct device *master, void *data) +{ + static struct adreno_platform_config config = {}; +#ifdef CONFIG_OF + struct device_node *child, *node = dev->of_node; + u32 val; + int ret; + + ret = of_property_read_u32(node, "qcom,chipid", &val); + if (ret) { + dev_err(dev, "could not find chipid: %d\n", ret); + return ret; + } + + config.rev = ADRENO_REV((val >> 24) & 0xff, + (val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff); + + /* find clock rates: */ + config.fast_rate = 0; + config.slow_rate = ~0; + for_each_child_of_node(node, child) { + if (of_device_is_compatible(child, "qcom,gpu-pwrlevels")) { + struct device_node *pwrlvl; + for_each_child_of_node(child, pwrlvl) { + ret = of_property_read_u32(pwrlvl, "qcom,gpu-freq", &val); + if (ret) { + dev_err(dev, "could not find gpu-freq: %d\n", ret); + return ret; + } + config.fast_rate = max(config.fast_rate, val); + config.slow_rate = min(config.slow_rate, val); + } + } + } + + if (!config.fast_rate) { + dev_err(dev, "could not find clk rates\n"); + return -ENXIO; + } + +#else + struct kgsl_device_platform_data *pdata = dev->platform_data; + uint32_t version = socinfo_get_version(); + if (cpu_is_apq8064ab()) { + config.fast_rate = 450000000; + config.slow_rate = 27000000; + config.bus_freq = 4; + config.rev = ADRENO_REV(3, 2, 1, 0); + } else if (cpu_is_apq8064()) { + config.fast_rate = 400000000; + config.slow_rate = 27000000; + config.bus_freq = 4; + + if (SOCINFO_VERSION_MAJOR(version) == 2) + config.rev = ADRENO_REV(3, 2, 0, 2); + else if ((SOCINFO_VERSION_MAJOR(version) == 1) && + (SOCINFO_VERSION_MINOR(version) == 1)) + config.rev = ADRENO_REV(3, 2, 0, 1); + else + config.rev = ADRENO_REV(3, 2, 0, 0); + + } else if (cpu_is_msm8960ab()) { + config.fast_rate = 400000000; + config.slow_rate = 320000000; + config.bus_freq = 4; + + if (SOCINFO_VERSION_MINOR(version) == 0) + config.rev = ADRENO_REV(3, 2, 1, 0); + else + config.rev = ADRENO_REV(3, 2, 1, 1); + + } else if (cpu_is_msm8930()) { + config.fast_rate = 400000000; + config.slow_rate = 27000000; + config.bus_freq = 3; + + if ((SOCINFO_VERSION_MAJOR(version) == 1) && + (SOCINFO_VERSION_MINOR(version) == 2)) + config.rev = ADRENO_REV(3, 0, 5, 2); + else + config.rev = ADRENO_REV(3, 0, 5, 0); + + } +# ifdef CONFIG_MSM_BUS_SCALING + config.bus_scale_table = pdata->bus_scale_table; +# endif +#endif + dev->platform_data = &config; + set_gpu_pdev(dev_get_drvdata(master), to_platform_device(dev)); + return 0; +} + +static void adreno_unbind(struct device *dev, struct device *master, + void *data) +{ + set_gpu_pdev(dev_get_drvdata(master), NULL); +} + +static const struct component_ops a3xx_ops = { + .bind = adreno_bind, + .unbind = adreno_unbind, +}; + +static int adreno_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &a3xx_ops); +} + +static int adreno_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &a3xx_ops); + return 0; +} + +static const struct of_device_id dt_match[] = { + { .compatible = "qcom,adreno-3xx" }, + /* for backwards compat w/ downstream kgsl DT files: */ + { .compatible = "qcom,kgsl-3d0" }, + {} +}; + +static struct platform_driver adreno_driver = { + .probe = adreno_probe, + .remove = adreno_remove, + .driver = { + .name = "adreno", + .of_match_table = dt_match, + }, +}; + +void __init adreno_register(void) +{ + platform_driver_register(&adreno_driver); +} + +void __exit adreno_unregister(void) +{ + platform_driver_unregister(&adreno_driver); +} diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 655ce5b14ad04564a5fb73e72e26b338bb3fb4e3..6afa29167fee74fffaae26e8d7ded15d05ec87eb 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -19,46 +19,6 @@ #include "msm_gem.h" #include "msm_mmu.h" -struct adreno_info { - struct adreno_rev rev; - uint32_t revn; - const char *name; - const char *pm4fw, *pfpfw; - uint32_t gmem; -}; - -#define ANY_ID 0xff - -static const struct adreno_info gpulist[] = { - { - .rev = ADRENO_REV(3, 0, 5, ANY_ID), - .revn = 305, - .name = "A305", - .pm4fw = "a300_pm4.fw", - .pfpfw = "a300_pfp.fw", - .gmem = SZ_256K, - }, { - .rev = ADRENO_REV(3, 2, ANY_ID, ANY_ID), - .revn = 320, - .name = "A320", - .pm4fw = "a300_pm4.fw", - .pfpfw = "a300_pfp.fw", - .gmem = SZ_512K, - }, { - .rev = ADRENO_REV(3, 3, 0, ANY_ID), - .revn = 330, - .name = "A330", - .pm4fw = "a330_pm4.fw", - .pfpfw = "a330_pfp.fw", - .gmem = SZ_1M, - }, -}; - -MODULE_FIRMWARE("a300_pm4.fw"); -MODULE_FIRMWARE("a300_pfp.fw"); -MODULE_FIRMWARE("a330_pm4.fw"); -MODULE_FIRMWARE("a330_pfp.fw"); - #define RB_SIZE SZ_32K #define RB_BLKSIZE 16 @@ -252,6 +212,7 @@ void adreno_idle(struct msm_gpu *gpu) void adreno_show(struct msm_gpu *gpu, struct seq_file *m) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + int i; seq_printf(m, "revision: %d (%d.%d.%d.%d)\n", adreno_gpu->info->revn, adreno_gpu->rev.core, @@ -263,6 +224,23 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m) seq_printf(m, "rptr: %d\n", adreno_gpu->memptrs->rptr); seq_printf(m, "wptr: %d\n", adreno_gpu->memptrs->wptr); seq_printf(m, "rb wptr: %d\n", get_wptr(gpu->rb)); + + gpu->funcs->pm_resume(gpu); + + /* dump these out in a form that can be parsed by demsm: */ + seq_printf(m, "IO:region %s 00000000 00020000\n", gpu->name); + for (i = 0; adreno_gpu->registers[i] != ~0; i += 2) { + uint32_t start = adreno_gpu->registers[i]; + uint32_t end = adreno_gpu->registers[i+1]; + uint32_t addr; + + for (addr = start; addr <= end; addr++) { + uint32_t val = gpu_read(gpu, addr); + seq_printf(m, "IO:R %08x %08x\n", addr<<2, val); + } + } + + gpu->funcs->pm_suspend(gpu); } #endif @@ -270,6 +248,7 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m) void adreno_dump(struct msm_gpu *gpu) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + int i; printk("revision: %d (%d.%d.%d.%d)\n", adreno_gpu->info->revn, adreno_gpu->rev.core, @@ -282,6 +261,18 @@ void adreno_dump(struct msm_gpu *gpu) printk("wptr: %d\n", adreno_gpu->memptrs->wptr); printk("rb wptr: %d\n", get_wptr(gpu->rb)); + /* dump these out in a form that can be parsed by demsm: */ + printk("IO:region %s 00000000 00020000\n", gpu->name); + for (i = 0; adreno_gpu->registers[i] != ~0; i += 2) { + uint32_t start = adreno_gpu->registers[i]; + uint32_t end = adreno_gpu->registers[i+1]; + uint32_t addr; + + for (addr = start; addr <= end; addr++) { + uint32_t val = gpu_read(gpu, addr); + printk("IO:R %08x %08x\n", addr<<2, val); + } + } } static uint32_t ring_freewords(struct msm_gpu *gpu) @@ -304,65 +295,51 @@ static const char *iommu_ports[] = { "gfx3d1_user", "gfx3d1_priv", }; -static inline bool _rev_match(uint8_t entry, uint8_t id) -{ - return (entry == ANY_ID) || (entry == id); -} - int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, - struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs, - struct adreno_rev rev) + struct adreno_gpu *adreno_gpu, const struct adreno_gpu_funcs *funcs) { + struct adreno_platform_config *config = pdev->dev.platform_data; + struct msm_gpu *gpu = &adreno_gpu->base; struct msm_mmu *mmu; - int i, ret; - - /* identify gpu: */ - for (i = 0; i < ARRAY_SIZE(gpulist); i++) { - const struct adreno_info *info = &gpulist[i]; - if (_rev_match(info->rev.core, rev.core) && - _rev_match(info->rev.major, rev.major) && - _rev_match(info->rev.minor, rev.minor) && - _rev_match(info->rev.patchid, rev.patchid)) { - gpu->info = info; - gpu->revn = info->revn; - break; - } - } - - if (i == ARRAY_SIZE(gpulist)) { - dev_err(drm->dev, "Unknown GPU revision: %u.%u.%u.%u\n", - rev.core, rev.major, rev.minor, rev.patchid); - return -ENXIO; - } + int ret; - DBG("Found GPU: %s (%u.%u.%u.%u)", gpu->info->name, - rev.core, rev.major, rev.minor, rev.patchid); + adreno_gpu->funcs = funcs; + adreno_gpu->info = adreno_info(config->rev); + adreno_gpu->gmem = adreno_gpu->info->gmem; + adreno_gpu->revn = adreno_gpu->info->revn; + adreno_gpu->rev = config->rev; + + gpu->fast_rate = config->fast_rate; + gpu->slow_rate = config->slow_rate; + gpu->bus_freq = config->bus_freq; +#ifdef CONFIG_MSM_BUS_SCALING + gpu->bus_scale_table = config->bus_scale_table; +#endif - gpu->funcs = funcs; - gpu->gmem = gpu->info->gmem; - gpu->rev = rev; + DBG("fast_rate=%u, slow_rate=%u, bus_freq=%u", + gpu->fast_rate, gpu->slow_rate, gpu->bus_freq); - ret = request_firmware(&gpu->pm4, gpu->info->pm4fw, drm->dev); + ret = request_firmware(&adreno_gpu->pm4, adreno_gpu->info->pm4fw, drm->dev); if (ret) { dev_err(drm->dev, "failed to load %s PM4 firmware: %d\n", - gpu->info->pm4fw, ret); + adreno_gpu->info->pm4fw, ret); return ret; } - ret = request_firmware(&gpu->pfp, gpu->info->pfpfw, drm->dev); + ret = request_firmware(&adreno_gpu->pfp, adreno_gpu->info->pfpfw, drm->dev); if (ret) { dev_err(drm->dev, "failed to load %s PFP firmware: %d\n", - gpu->info->pfpfw, ret); + adreno_gpu->info->pfpfw, ret); return ret; } - ret = msm_gpu_init(drm, pdev, &gpu->base, &funcs->base, - gpu->info->name, "kgsl_3d0_reg_memory", "kgsl_3d0_irq", + ret = msm_gpu_init(drm, pdev, &adreno_gpu->base, &funcs->base, + adreno_gpu->info->name, "kgsl_3d0_reg_memory", "kgsl_3d0_irq", RB_SIZE); if (ret) return ret; - mmu = gpu->base.mmu; + mmu = gpu->mmu; if (mmu) { ret = mmu->funcs->attach(mmu, iommu_ports, ARRAY_SIZE(iommu_ports)); @@ -371,24 +348,24 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, } mutex_lock(&drm->struct_mutex); - gpu->memptrs_bo = msm_gem_new(drm, sizeof(*gpu->memptrs), + adreno_gpu->memptrs_bo = msm_gem_new(drm, sizeof(*adreno_gpu->memptrs), MSM_BO_UNCACHED); mutex_unlock(&drm->struct_mutex); - if (IS_ERR(gpu->memptrs_bo)) { - ret = PTR_ERR(gpu->memptrs_bo); - gpu->memptrs_bo = NULL; + if (IS_ERR(adreno_gpu->memptrs_bo)) { + ret = PTR_ERR(adreno_gpu->memptrs_bo); + adreno_gpu->memptrs_bo = NULL; dev_err(drm->dev, "could not allocate memptrs: %d\n", ret); return ret; } - gpu->memptrs = msm_gem_vaddr(gpu->memptrs_bo); - if (!gpu->memptrs) { + adreno_gpu->memptrs = msm_gem_vaddr(adreno_gpu->memptrs_bo); + if (!adreno_gpu->memptrs) { dev_err(drm->dev, "could not vmap memptrs\n"); return -ENOMEM; } - ret = msm_gem_get_iova(gpu->memptrs_bo, gpu->base.id, - &gpu->memptrs_iova); + ret = msm_gem_get_iova(adreno_gpu->memptrs_bo, gpu->id, + &adreno_gpu->memptrs_iova); if (ret) { dev_err(drm->dev, "could not map memptrs: %d\n", ret); return ret; diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index 63c36ce330203669b597ff6a6e606a6066ee3832..52f0515797532cef0d4496918fe421306946d0a5 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -39,7 +39,16 @@ struct adreno_gpu_funcs { struct msm_gpu_funcs base; }; -struct adreno_info; +struct adreno_info { + struct adreno_rev rev; + uint32_t revn; + const char *name; + const char *pm4fw, *pfpfw; + uint32_t gmem; + struct msm_gpu *(*init)(struct drm_device *dev); +}; + +const struct adreno_info *adreno_info(struct adreno_rev rev); struct adreno_rbmemptrs { volatile uint32_t rptr; @@ -55,6 +64,9 @@ struct adreno_gpu { uint32_t revn; /* numeric revision name */ const struct adreno_gpu_funcs *funcs; + /* interesting register offsets to dump: */ + const unsigned int *registers; + /* firmware: */ const struct firmware *pm4, *pfp; @@ -131,8 +143,7 @@ void adreno_dump(struct msm_gpu *gpu); void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords); int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, - struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs, - struct adreno_rev rev); + struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs); void adreno_gpu_cleanup(struct adreno_gpu *gpu); diff --git a/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h b/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h index 4eee0ec8f069108a32a94f239db78f2fda50cd9b..6ef43f66c30a37676b9231d8f9b202b5f50c49f3 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h +++ b/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h @@ -12,9 +12,9 @@ The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) - /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2014-06-02 15:21:30) - /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 9859 bytes, from 2014-06-02 15:21:30) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14477 bytes, from 2014-05-16 11:51:57) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 58020 bytes, from 2014-06-25 12:57:16) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 26602 bytes, from 2014-06-25 12:57:16) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14960 bytes, from 2014-07-27 17:22:13) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 58020 bytes, from 2014-08-01 12:22:48) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 41068 bytes, from 2014-08-01 12:22:48) Copyright (C) 2013-2014 by the following authors: - Rob Clark (robclark) @@ -163,12 +163,16 @@ enum adreno_pm4_type3_packets { CP_INDIRECT_BUFFER_PFE = 63, CP_SET_BIN = 76, CP_TEST_TWO_MEMS = 113, + CP_REG_WR_NO_CTXT = 120, + CP_RECORD_PFP_TIMESTAMP = 17, CP_WAIT_FOR_ME = 19, CP_SET_DRAW_STATE = 67, CP_DRAW_INDX_OFFSET = 56, CP_DRAW_INDIRECT = 40, CP_DRAW_INDX_INDIRECT = 41, CP_DRAW_AUTO = 36, + CP_UNKNOWN_1A = 26, + CP_WIDE_REG_WRITE = 116, IN_IB_PREFETCH_END = 23, IN_SUBBLK_PREFETCH = 31, IN_INSTR_PREFETCH = 32, diff --git a/drivers/gpu/drm/msm/dsi/dsi.xml.h b/drivers/gpu/drm/msm/dsi/dsi.xml.h index 0f1f5b9459a51de71dad5b75fbcede35762432f5..e965898dfda6cbc2b09876fe3a9f8b68582d8fc5 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.xml.h +++ b/drivers/gpu/drm/msm/dsi/dsi.xml.h @@ -10,14 +10,14 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 647 bytes, from 2013-11-30 14:45:35) - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 17996 bytes, from 2013-12-01 19:10:31) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2013-11-30 15:00:52) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-06-25 12:55:02) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20457 bytes, from 2014-08-01 12:22:48) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2014-07-17 15:34:33) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-07-17 15:34:33) - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43) - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2014-08-01 12:23:53) - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-06-25 12:53:44) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-07-17 15:33:30) Copyright (C) 2013 by the following authors: - Rob Clark (robclark) diff --git a/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h b/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h index d468f86f637cece324dc46299bbe19022cbb484f..f2bdda957205a092b4d4a0bd6f02cfcd0d32fdb6 100644 --- a/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h +++ b/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h @@ -10,16 +10,16 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 647 bytes, from 2013-11-30 14:45:35) - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 17996 bytes, from 2013-12-01 19:10:31) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2013-11-30 15:00:52) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-06-25 12:55:02) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20457 bytes, from 2014-08-01 12:22:48) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2014-07-17 15:34:33) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-07-17 15:34:33) - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43) - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2014-08-01 12:23:53) - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-06-25 12:53:44) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-07-17 15:33:30) -Copyright (C) 2013 by the following authors: +Copyright (C) 2013-2014 by the following authors: - Rob Clark (robclark) Permission is hereby granted, free of charge, to any person obtaining @@ -112,5 +112,11 @@ static inline uint32_t MMSS_CC_CLK_NS_VAL(uint32_t val) return ((val) << MMSS_CC_CLK_NS_VAL__SHIFT) & MMSS_CC_CLK_NS_VAL__MASK; } +#define REG_MMSS_CC_DSI2_PIXEL_CC 0x00000094 + +#define REG_MMSS_CC_DSI2_PIXEL_NS 0x000000e4 + +#define REG_MMSS_CC_DSI2_PIXEL_CC2 0x00000264 + #endif /* MMSS_CC_XML */ diff --git a/drivers/gpu/drm/msm/dsi/sfpb.xml.h b/drivers/gpu/drm/msm/dsi/sfpb.xml.h index da8740054cdf264bef41396db1e8ca09964bf6bc..e5b071ffd8657761d78f8499f86eb5c4e825144b 100644 --- a/drivers/gpu/drm/msm/dsi/sfpb.xml.h +++ b/drivers/gpu/drm/msm/dsi/sfpb.xml.h @@ -10,14 +10,14 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 647 bytes, from 2013-11-30 14:45:35) - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 17996 bytes, from 2013-12-01 19:10:31) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2013-11-30 15:00:52) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-06-25 12:55:02) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20457 bytes, from 2014-08-01 12:22:48) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2014-07-17 15:34:33) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-07-17 15:34:33) - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43) - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2014-08-01 12:23:53) - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-06-25 12:53:44) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-07-17 15:33:30) Copyright (C) 2013 by the following authors: - Rob Clark (robclark) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index c6c9b02e0ada9f052249a556a9e2169aa967b91d..9d00dcba695950873c77baa761dea89e4b86aeac 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -123,7 +123,7 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder) for (i = 0; i < config->hpd_reg_cnt; i++) { struct regulator *reg; - reg = devm_regulator_get_exclusive(&pdev->dev, + reg = devm_regulator_get(&pdev->dev, config->hpd_reg_names[i]); if (IS_ERR(reg)) { ret = PTR_ERR(reg); @@ -139,7 +139,7 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder) for (i = 0; i < config->pwr_reg_cnt; i++) { struct regulator *reg; - reg = devm_regulator_get_exclusive(&pdev->dev, + reg = devm_regulator_get(&pdev->dev, config->pwr_reg_names[i]); if (IS_ERR(reg)) { ret = PTR_ERR(reg); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h index e89fe053d375763b3252f3d6fba8e1fcad90d8f7..76fd0cfc6558fc622755ca90ff4c3fbc9a4f17ea 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h @@ -10,14 +10,14 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 647 bytes, from 2013-11-30 14:45:35) - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 17996 bytes, from 2013-12-01 19:10:31) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2013-11-30 15:00:52) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-06-25 12:55:02) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20457 bytes, from 2014-08-01 12:22:48) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2014-07-17 15:34:33) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-07-17 15:34:33) - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43) - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2014-08-01 12:23:53) - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-06-25 12:53:44) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-07-17 15:33:30) Copyright (C) 2013-2014 by the following authors: - Rob Clark (robclark) diff --git a/drivers/gpu/drm/msm/hdmi/qfprom.xml.h b/drivers/gpu/drm/msm/hdmi/qfprom.xml.h index bd81db6a7829f999fd183a096e86dea8c88d1761..d53c29327df908c81e957467803ce0d6d6ad3ece 100644 --- a/drivers/gpu/drm/msm/hdmi/qfprom.xml.h +++ b/drivers/gpu/drm/msm/hdmi/qfprom.xml.h @@ -10,14 +10,14 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 647 bytes, from 2013-11-30 14:45:35) - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 17996 bytes, from 2013-12-01 19:10:31) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2013-11-30 15:00:52) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-06-25 12:55:02) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20457 bytes, from 2014-08-01 12:22:48) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2014-07-17 15:34:33) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-07-17 15:34:33) - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43) - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2014-08-01 12:23:53) - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-06-25 12:53:44) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-07-17 15:33:30) Copyright (C) 2013 by the following authors: - Rob Clark (robclark) diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h index 122208e8a2ee205d018f9d2c55ebd92de1af756b..03c0bd9cd5b9270dcec078aaa71ce7be2adff210 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h @@ -10,16 +10,16 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 647 bytes, from 2013-11-30 14:45:35) - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 17996 bytes, from 2013-12-01 19:10:31) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2013-11-30 15:00:52) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-06-25 12:55:02) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20457 bytes, from 2014-08-01 12:22:48) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2014-07-17 15:34:33) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-07-17 15:34:33) - /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43) - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2014-08-01 12:23:53) - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-06-25 12:53:44) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-07-17 15:33:30) -Copyright (C) 2013 by the following authors: +Copyright (C) 2013-2014 by the following authors: - Rob Clark (robclark) Permission is hereby granted, free of charge, to any person obtaining @@ -871,6 +871,101 @@ static inline uint32_t MDP4_LCDC_UNDERFLOW_CLR_COLOR(uint32_t val) #define MDP4_LCDC_CTRL_POLARITY_VSYNC_LOW 0x00000002 #define MDP4_LCDC_CTRL_POLARITY_DATA_EN_LOW 0x00000004 +#define REG_MDP4_LCDC_LVDS_INTF_CTL 0x000c2000 +#define MDP4_LCDC_LVDS_INTF_CTL_MODE_SEL 0x00000004 +#define MDP4_LCDC_LVDS_INTF_CTL_RGB_OUT 0x00000008 +#define MDP4_LCDC_LVDS_INTF_CTL_CH_SWAP 0x00000010 +#define MDP4_LCDC_LVDS_INTF_CTL_CH1_RES_BIT 0x00000020 +#define MDP4_LCDC_LVDS_INTF_CTL_CH2_RES_BIT 0x00000040 +#define MDP4_LCDC_LVDS_INTF_CTL_ENABLE 0x00000080 +#define MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN 0x00000100 +#define MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN 0x00000200 +#define MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN 0x00000400 +#define MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE3_EN 0x00000800 +#define MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE0_EN 0x00001000 +#define MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE1_EN 0x00002000 +#define MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE2_EN 0x00004000 +#define MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE3_EN 0x00008000 +#define MDP4_LCDC_LVDS_INTF_CTL_CH1_CLK_LANE_EN 0x00010000 +#define MDP4_LCDC_LVDS_INTF_CTL_CH2_CLK_LANE_EN 0x00020000 + +static inline uint32_t REG_MDP4_LCDC_LVDS_MUX_CTL(uint32_t i0) { return 0x000c2014 + 0x8*i0; } + +static inline uint32_t REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(uint32_t i0) { return 0x000c2014 + 0x8*i0; } +#define MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0__MASK 0x000000ff +#define MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0__SHIFT 0 +static inline uint32_t MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(uint32_t val) +{ + return ((val) << MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0__SHIFT) & MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0__MASK; +} +#define MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1__MASK 0x0000ff00 +#define MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1__SHIFT 8 +static inline uint32_t MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(uint32_t val) +{ + return ((val) << MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1__SHIFT) & MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1__MASK; +} +#define MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2__MASK 0x00ff0000 +#define MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2__SHIFT 16 +static inline uint32_t MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(uint32_t val) +{ + return ((val) << MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2__SHIFT) & MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2__MASK; +} +#define MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3__MASK 0xff000000 +#define MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3__SHIFT 24 +static inline uint32_t MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(uint32_t val) +{ + return ((val) << MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3__SHIFT) & MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3__MASK; +} + +static inline uint32_t REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(uint32_t i0) { return 0x000c2018 + 0x8*i0; } +#define MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4__MASK 0x000000ff +#define MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4__SHIFT 0 +static inline uint32_t MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(uint32_t val) +{ + return ((val) << MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4__SHIFT) & MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4__MASK; +} +#define MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5__MASK 0x0000ff00 +#define MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5__SHIFT 8 +static inline uint32_t MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(uint32_t val) +{ + return ((val) << MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5__SHIFT) & MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5__MASK; +} +#define MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6__MASK 0x00ff0000 +#define MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6__SHIFT 16 +static inline uint32_t MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(uint32_t val) +{ + return ((val) << MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6__SHIFT) & MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6__MASK; +} + +#define REG_MDP4_LCDC_LVDS_PHY_RESET 0x000c2034 + +#define REG_MDP4_LVDS_PHY_PLL_CTRL_0 0x000c3000 + +#define REG_MDP4_LVDS_PHY_PLL_CTRL_1 0x000c3004 + +#define REG_MDP4_LVDS_PHY_PLL_CTRL_2 0x000c3008 + +#define REG_MDP4_LVDS_PHY_PLL_CTRL_3 0x000c300c + +#define REG_MDP4_LVDS_PHY_PLL_CTRL_5 0x000c3014 + +#define REG_MDP4_LVDS_PHY_PLL_CTRL_6 0x000c3018 + +#define REG_MDP4_LVDS_PHY_PLL_CTRL_7 0x000c301c + +#define REG_MDP4_LVDS_PHY_PLL_CTRL_8 0x000c3020 + +#define REG_MDP4_LVDS_PHY_PLL_CTRL_9 0x000c3024 + +#define REG_MDP4_LVDS_PHY_PLL_LOCKED 0x000c3080 + +#define REG_MDP4_LVDS_PHY_CFG2 0x000c3108 + +#define REG_MDP4_LVDS_PHY_CFG0 0x000c3100 +#define MDP4_LVDS_PHY_CFG0_SERIALIZATION_ENBLE 0x00000010 +#define MDP4_LVDS_PHY_CFG0_CHANNEL0 0x00000040 +#define MDP4_LVDS_PHY_CFG0_CHANNEL1 0x00000080 + #define REG_MDP4_DTV 0x000d0000 #define REG_MDP4_DTV_ENABLE 0x000d0000 diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c index c6c80ea28c35809dbe63bb22aec0a50da9f4d0c6..7d00f7fb5773571c2fd005df0e278c18de425b64 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -273,14 +273,17 @@ static void blend_setup(struct drm_crtc *crtc) }; bool alpha[4]= { false, false, false, false }; + /* Don't rely on value read back from hw, but instead use our + * own shadowed value. Possibly disable/reenable looses the + * previous value and goes back to power-on default? + */ + mixer_cfg = mdp4_kms->mixer_cfg; + mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_LOW0(ovlp), 0); mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_LOW1(ovlp), 0); mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH0(ovlp), 0); mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH1(ovlp), 0); - /* TODO single register for all CRTCs, so this won't work properly - * when multiple CRTCs are active.. - */ for (i = 0; i < ARRAY_SIZE(mdp4_crtc->planes); i++) { struct drm_plane *plane = mdp4_crtc->planes[i]; if (plane) { @@ -291,7 +294,8 @@ static void blend_setup(struct drm_crtc *crtc) to_mdp_format(msm_framebuffer_format(plane->fb)); alpha[idx-1] = format->alpha_enable; } - mixer_cfg |= mixercfg(mdp4_crtc->mixer, pipe_id, stages[idx]); + mixer_cfg = mixercfg(mixer_cfg, mdp4_crtc->mixer, + pipe_id, stages[idx]); } } @@ -320,6 +324,7 @@ static void blend_setup(struct drm_crtc *crtc) mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_HIGH1(ovlp, i), 0); } + mdp4_kms->mixer_cfg = mixer_cfg; mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG, mixer_cfg); } @@ -672,7 +677,7 @@ void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config) } /* set interface for routing crtc->encoder: */ -void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf) +void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf, int mixer) { struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); struct mdp4_kms *mdp4_kms = get_kms(crtc); @@ -698,15 +703,13 @@ void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf) if (intf == INTF_DSI_VIDEO) { intf_sel &= ~MDP4_DISP_INTF_SEL_DSI_CMD; intf_sel |= MDP4_DISP_INTF_SEL_DSI_VIDEO; - mdp4_crtc->mixer = 0; } else if (intf == INTF_DSI_CMD) { intf_sel &= ~MDP4_DISP_INTF_SEL_DSI_VIDEO; intf_sel |= MDP4_DISP_INTF_SEL_DSI_CMD; - mdp4_crtc->mixer = 0; - } else if (intf == INTF_LCDC_DTV){ - mdp4_crtc->mixer = 1; } + mdp4_crtc->mixer = mixer; + blend_setup(crtc); DBG("%s: intf_sel=%08x", mdp4_crtc->name, intf_sel); diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c index 067ed03b35fef964f530746a877efb77bf9fcbe5..c3878420180b82f4e0a0bf1c75d13e33432c51ee 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c @@ -233,7 +233,7 @@ static void mdp4_dtv_encoder_commit(struct drm_encoder *encoder) MDP4_DMA_CONFIG_G_BPC(BPC8) | MDP4_DMA_CONFIG_B_BPC(BPC8) | MDP4_DMA_CONFIG_PACK(0x21)); - mdp4_crtc_set_intf(encoder->crtc, INTF_LCDC_DTV); + mdp4_crtc_set_intf(encoder->crtc, INTF_LCDC_DTV, 1); mdp4_dtv_encoder_dpms(encoder, DRM_MODE_DPMS_ON); } diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index 733646c0d3f871cc090efedb741fb8ae73ec790f..79d804e61cc44e48710f7a4703f38008e7a37b6c 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -106,6 +106,7 @@ static int mdp4_hw_init(struct msm_kms *kms) if (mdp4_kms->rev >= 2) mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG_UPDATE_METHOD, 1); + mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG, 0); /* disable CSC matrix / YUV by default: */ mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(VG1), 0); @@ -196,6 +197,28 @@ int mdp4_enable(struct mdp4_kms *mdp4_kms) return 0; } +#ifdef CONFIG_OF +static struct drm_panel *detect_panel(struct drm_device *dev, const char *name) +{ + struct device_node *n; + struct drm_panel *panel = NULL; + + n = of_parse_phandle(dev->dev->of_node, name, 0); + if (n) { + panel = of_drm_find_panel(n); + if (!panel) + panel = ERR_PTR(-EPROBE_DEFER); + } + + return panel; +} +#else +static struct drm_panel *detect_panel(struct drm_device *dev, const char *name) +{ + // ??? maybe use a module param to specify which panel is attached? +} +#endif + static int modeset_init(struct mdp4_kms *mdp4_kms) { struct drm_device *dev = mdp4_kms->dev; @@ -203,14 +226,11 @@ static int modeset_init(struct mdp4_kms *mdp4_kms) struct drm_plane *plane; struct drm_crtc *crtc; struct drm_encoder *encoder; + struct drm_connector *connector; + struct drm_panel *panel; struct hdmi *hdmi; int ret; - /* - * NOTE: this is a bit simplistic until we add support - * for more than just RGB1->DMA_E->DTV->HDMI - */ - /* construct non-private planes: */ plane = mdp4_plane_init(dev, VG1, false); if (IS_ERR(plane)) { @@ -228,7 +248,57 @@ static int modeset_init(struct mdp4_kms *mdp4_kms) } priv->planes[priv->num_planes++] = plane; - /* the CRTCs get constructed with a private plane: */ + /* + * Setup the LCDC/LVDS path: RGB2 -> DMA_P -> LCDC -> LVDS: + */ + + panel = detect_panel(dev, "qcom,lvds-panel"); + if (IS_ERR(panel)) { + ret = PTR_ERR(panel); + dev_err(dev->dev, "failed to detect LVDS panel: %d\n", ret); + goto fail; + } + + plane = mdp4_plane_init(dev, RGB2, true); + if (IS_ERR(plane)) { + dev_err(dev->dev, "failed to construct plane for RGB2\n"); + ret = PTR_ERR(plane); + goto fail; + } + + crtc = mdp4_crtc_init(dev, plane, priv->num_crtcs, 0, DMA_P); + if (IS_ERR(crtc)) { + dev_err(dev->dev, "failed to construct crtc for DMA_P\n"); + ret = PTR_ERR(crtc); + goto fail; + } + + encoder = mdp4_lcdc_encoder_init(dev, panel); + if (IS_ERR(encoder)) { + dev_err(dev->dev, "failed to construct LCDC encoder\n"); + ret = PTR_ERR(encoder); + goto fail; + } + + /* LCDC can be hooked to DMA_P: */ + encoder->possible_crtcs = 1 << priv->num_crtcs; + + priv->crtcs[priv->num_crtcs++] = crtc; + priv->encoders[priv->num_encoders++] = encoder; + + connector = mdp4_lvds_connector_init(dev, panel, encoder); + if (IS_ERR(connector)) { + ret = PTR_ERR(connector); + dev_err(dev->dev, "failed to initialize LVDS connector: %d\n", ret); + goto fail; + } + + priv->connectors[priv->num_connectors++] = connector; + + /* + * Setup DTV/HDMI path: RGB1 -> DMA_E -> DTV -> HDMI: + */ + plane = mdp4_plane_init(dev, RGB1, true); if (IS_ERR(plane)) { dev_err(dev->dev, "failed to construct plane for RGB1\n"); @@ -242,7 +312,6 @@ static int modeset_init(struct mdp4_kms *mdp4_kms) ret = PTR_ERR(crtc); goto fail; } - priv->crtcs[priv->num_crtcs++] = crtc; encoder = mdp4_dtv_encoder_init(dev); if (IS_ERR(encoder)) { @@ -250,7 +319,11 @@ static int modeset_init(struct mdp4_kms *mdp4_kms) ret = PTR_ERR(encoder); goto fail; } - encoder->possible_crtcs = 0x1; /* DTV can be hooked to DMA_E */ + + /* DTV can be hooked to DMA_E: */ + encoder->possible_crtcs = 1 << priv->num_crtcs; + + priv->crtcs[priv->num_crtcs++] = crtc; priv->encoders[priv->num_encoders++] = encoder; hdmi = hdmi_init(dev, encoder); diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h index 3225da804c613f4f9fce06960862bb6bb345b406..9ff6e7ccfe90d248fc70a2ccaabba6d92d7aab0a 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h @@ -23,6 +23,8 @@ #include "mdp/mdp_kms.h" #include "mdp4.xml.h" +#include "drm_panel.h" + struct mdp4_kms { struct mdp_kms base; @@ -30,6 +32,13 @@ struct mdp4_kms { int rev; + /* Shadow value for MDP4_LAYERMIXER_IN_CFG.. since setup for all + * crtcs/encoders is in one shared register, we need to update it + * via read/modify/write. But to avoid getting confused by power- + * on-default values after resume, use this shadow value instead: + */ + uint32_t mixer_cfg; + /* mapper-id used to request GEM buffer mapped for scanout: */ int id; @@ -74,7 +83,7 @@ static inline uint32_t pipe2flush(enum mdp4_pipe pipe) case VG1: return MDP4_OVERLAY_FLUSH_VG1; case VG2: return MDP4_OVERLAY_FLUSH_VG2; case RGB1: return MDP4_OVERLAY_FLUSH_RGB1; - case RGB2: return MDP4_OVERLAY_FLUSH_RGB1; + case RGB2: return MDP4_OVERLAY_FLUSH_RGB2; default: return 0; } } @@ -108,38 +117,50 @@ static inline uint32_t dma2err(enum mdp4_dma dma) } } -static inline uint32_t mixercfg(int mixer, enum mdp4_pipe pipe, - enum mdp_mixer_stage_id stage) +static inline uint32_t mixercfg(uint32_t mixer_cfg, int mixer, + enum mdp4_pipe pipe, enum mdp_mixer_stage_id stage) { - uint32_t mixer_cfg = 0; - switch (pipe) { case VG1: - mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE0(stage) | + mixer_cfg &= ~(MDP4_LAYERMIXER_IN_CFG_PIPE0__MASK | + MDP4_LAYERMIXER_IN_CFG_PIPE0_MIXER1); + mixer_cfg |= MDP4_LAYERMIXER_IN_CFG_PIPE0(stage) | COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE0_MIXER1); break; case VG2: - mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE1(stage) | + mixer_cfg &= ~(MDP4_LAYERMIXER_IN_CFG_PIPE1__MASK | + MDP4_LAYERMIXER_IN_CFG_PIPE1_MIXER1); + mixer_cfg |= MDP4_LAYERMIXER_IN_CFG_PIPE1(stage) | COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE1_MIXER1); break; case RGB1: - mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE2(stage) | + mixer_cfg &= ~(MDP4_LAYERMIXER_IN_CFG_PIPE2__MASK | + MDP4_LAYERMIXER_IN_CFG_PIPE2_MIXER1); + mixer_cfg |= MDP4_LAYERMIXER_IN_CFG_PIPE2(stage) | COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE2_MIXER1); break; case RGB2: - mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE3(stage) | + mixer_cfg &= ~(MDP4_LAYERMIXER_IN_CFG_PIPE3__MASK | + MDP4_LAYERMIXER_IN_CFG_PIPE3_MIXER1); + mixer_cfg |= MDP4_LAYERMIXER_IN_CFG_PIPE3(stage) | COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE3_MIXER1); break; case RGB3: - mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE4(stage) | + mixer_cfg &= ~(MDP4_LAYERMIXER_IN_CFG_PIPE4__MASK | + MDP4_LAYERMIXER_IN_CFG_PIPE4_MIXER1); + mixer_cfg |= MDP4_LAYERMIXER_IN_CFG_PIPE4(stage) | COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE4_MIXER1); break; case VG3: - mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE5(stage) | + mixer_cfg &= ~(MDP4_LAYERMIXER_IN_CFG_PIPE5__MASK | + MDP4_LAYERMIXER_IN_CFG_PIPE5_MIXER1); + mixer_cfg |= MDP4_LAYERMIXER_IN_CFG_PIPE5(stage) | COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE5_MIXER1); break; case VG4: - mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE6(stage) | + mixer_cfg &= ~(MDP4_LAYERMIXER_IN_CFG_PIPE6__MASK | + MDP4_LAYERMIXER_IN_CFG_PIPE6_MIXER1); + mixer_cfg |= MDP4_LAYERMIXER_IN_CFG_PIPE6(stage) | COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE6_MIXER1); break; default: @@ -188,7 +209,7 @@ struct drm_plane *mdp4_plane_init(struct drm_device *dev, uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc); void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file); void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config); -void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf); +void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf, int mixer); void mdp4_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane); void mdp4_crtc_detach(struct drm_crtc *crtc, struct drm_plane *plane); struct drm_crtc *mdp4_crtc_init(struct drm_device *dev, @@ -198,6 +219,22 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev, long mdp4_dtv_round_pixclk(struct drm_encoder *encoder, unsigned long rate); struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev); +long mdp4_lcdc_round_pixclk(struct drm_encoder *encoder, unsigned long rate); +struct drm_encoder *mdp4_lcdc_encoder_init(struct drm_device *dev, + struct drm_panel *panel); + +struct drm_connector *mdp4_lvds_connector_init(struct drm_device *dev, + struct drm_panel *panel, struct drm_encoder *encoder); + +#ifdef CONFIG_COMMON_CLK +struct clk *mpd4_lvds_pll_init(struct drm_device *dev); +#else +static inline struct clk *mpd4_lvds_pll_init(struct drm_device *dev) +{ + return ERR_PTR(-ENODEV); +} +#endif + #ifdef CONFIG_MSM_BUS_SCALING static inline int match_dev_name(struct device *dev, void *data) { diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c new file mode 100644 index 0000000000000000000000000000000000000000..41f6436754fc8ff7138bdb4f260e3be1719805ce --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c @@ -0,0 +1,506 @@ +/* + * Copyright (C) 2014 Red Hat + * Author: Rob Clark + * Author: Vinay Simha + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "mdp4_kms.h" + +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +struct mdp4_lcdc_encoder { + struct drm_encoder base; + struct drm_panel *panel; + struct clk *lcdc_clk; + unsigned long int pixclock; + struct regulator *regs[3]; + bool enabled; + uint32_t bsc; +}; +#define to_mdp4_lcdc_encoder(x) container_of(x, struct mdp4_lcdc_encoder, base) + +static struct mdp4_kms *get_kms(struct drm_encoder *encoder) +{ + struct msm_drm_private *priv = encoder->dev->dev_private; + return to_mdp4_kms(to_mdp_kms(priv->kms)); +} + +#ifdef CONFIG_MSM_BUS_SCALING +#include +static void bs_init(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder) +{ + struct drm_device *dev = mdp4_lcdc_encoder->base.dev; + struct lcdc_platform_data *lcdc_pdata = mdp4_find_pdata("lvds.0"); + + if (!lcdc_pdata) { + dev_err(dev->dev, "could not find lvds pdata\n"); + return; + } + + if (lcdc_pdata->bus_scale_table) { + mdp4_lcdc_encoder->bsc = msm_bus_scale_register_client( + lcdc_pdata->bus_scale_table); + DBG("lvds : bus scale client: %08x", mdp4_lcdc_encoder->bsc); + } +} + +static void bs_fini(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder) +{ + if (mdp4_lcdc_encoder->bsc) { + msm_bus_scale_unregister_client(mdp4_lcdc_encoder->bsc); + mdp4_lcdc_encoder->bsc = 0; + } +} + +static void bs_set(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder, int idx) +{ + if (mdp4_lcdc_encoder->bsc) { + DBG("set bus scaling: %d", idx); + msm_bus_scale_client_update_request(mdp4_lcdc_encoder->bsc, idx); + } +} +#else +static void bs_init(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder) {} +static void bs_fini(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder) {} +static void bs_set(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder, int idx) {} +#endif + +static void mdp4_lcdc_encoder_destroy(struct drm_encoder *encoder) +{ + struct mdp4_lcdc_encoder *mdp4_lcdc_encoder = + to_mdp4_lcdc_encoder(encoder); + bs_fini(mdp4_lcdc_encoder); + drm_encoder_cleanup(encoder); + kfree(mdp4_lcdc_encoder); +} + +static const struct drm_encoder_funcs mdp4_lcdc_encoder_funcs = { + .destroy = mdp4_lcdc_encoder_destroy, +}; + +/* this should probably be a helper: */ +struct drm_connector *get_connector(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct drm_connector *connector; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) + if (connector->encoder == encoder) + return connector; + + return NULL; +} + +static void setup_phy(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct drm_connector *connector = get_connector(encoder); + struct mdp4_kms *mdp4_kms = get_kms(encoder); + uint32_t lvds_intf = 0, lvds_phy_cfg0 = 0; + int bpp, nchan, swap; + + if (!connector) + return; + + bpp = 3 * connector->display_info.bpc; + + if (!bpp) + bpp = 18; + + /* TODO, these should come from panel somehow: */ + nchan = 1; + swap = 0; + + switch (bpp) { + case 24: + mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(0), + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x08) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x05) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x04) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x03)); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(0), + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x02) | + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x01) | + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x00)); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(1), + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x11) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x10) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x0d) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0c)); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(1), + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0b) | + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x0a) | + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x09)); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(2), + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1a) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x19) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x18) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x15)); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(2), + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x14) | + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x13) | + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x12)); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(3), + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1b) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x17) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x16) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0f)); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(3), + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0e) | + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x07) | + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x06)); + if (nchan == 2) { + lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE3_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE2_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE1_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE0_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE3_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN; + } else { + lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE3_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN; + } + break; + + case 18: + mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(0), + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x0a) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x07) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x06) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x05)); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(0), + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x04) | + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x03) | + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x02)); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(1), + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x13) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x12) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x0f) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0e)); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(1), + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0d) | + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x0c) | + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x0b)); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(2), + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1a) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x19) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x18) | + MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x17)); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(2), + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x16) | + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x15) | + MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x14)); + if (nchan == 2) { + lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE2_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE1_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE0_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN; + } else { + lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN; + } + lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_RGB_OUT; + break; + + default: + dev_err(dev->dev, "unknown bpp: %d\n", bpp); + return; + } + + switch (nchan) { + case 1: + lvds_phy_cfg0 = MDP4_LVDS_PHY_CFG0_CHANNEL0; + lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_CLK_LANE_EN | + MDP4_LCDC_LVDS_INTF_CTL_MODE_SEL; + break; + case 2: + lvds_phy_cfg0 = MDP4_LVDS_PHY_CFG0_CHANNEL0 | + MDP4_LVDS_PHY_CFG0_CHANNEL1; + lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_CLK_LANE_EN | + MDP4_LCDC_LVDS_INTF_CTL_CH1_CLK_LANE_EN; + break; + default: + dev_err(dev->dev, "unknown # of channels: %d\n", nchan); + return; + } + + if (swap) + lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH_SWAP; + + lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_ENABLE; + + mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG0, lvds_phy_cfg0); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_INTF_CTL, lvds_intf); + mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG2, 0x30); + + mb(); + udelay(1); + lvds_phy_cfg0 |= MDP4_LVDS_PHY_CFG0_SERIALIZATION_ENBLE; + mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG0, lvds_phy_cfg0); +} + +static void mdp4_lcdc_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct mdp4_lcdc_encoder *mdp4_lcdc_encoder = + to_mdp4_lcdc_encoder(encoder); + struct mdp4_kms *mdp4_kms = get_kms(encoder); + struct drm_panel *panel = mdp4_lcdc_encoder->panel; + bool enabled = (mode == DRM_MODE_DPMS_ON); + int i, ret; + + DBG("mode=%d", mode); + + if (enabled == mdp4_lcdc_encoder->enabled) + return; + + if (enabled) { + unsigned long pc = mdp4_lcdc_encoder->pixclock; + int ret; + + bs_set(mdp4_lcdc_encoder, 1); + + for (i = 0; i < ARRAY_SIZE(mdp4_lcdc_encoder->regs); i++) { + ret = regulator_enable(mdp4_lcdc_encoder->regs[i]); + if (ret) + dev_err(dev->dev, "failed to enable regulator: %d\n", ret); + } + + DBG("setting lcdc_clk=%lu", pc); + ret = clk_set_rate(mdp4_lcdc_encoder->lcdc_clk, pc); + if (ret) + dev_err(dev->dev, "failed to configure lcdc_clk: %d\n", ret); + ret = clk_prepare_enable(mdp4_lcdc_encoder->lcdc_clk); + if (ret) + dev_err(dev->dev, "failed to enable lcdc_clk: %d\n", ret); + + if (panel) + drm_panel_enable(panel); + + setup_phy(encoder); + + mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 1); + } else { + mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0); + + if (panel) + drm_panel_disable(panel); + + /* + * Wait for a vsync so we know the ENABLE=0 latched before + * the (connector) source of the vsync's gets disabled, + * otherwise we end up in a funny state if we re-enable + * before the disable latches, which results that some of + * the settings changes for the new modeset (like new + * scanout buffer) don't latch properly.. + */ + mdp_irq_wait(&mdp4_kms->base, MDP4_IRQ_PRIMARY_VSYNC); + + clk_disable_unprepare(mdp4_lcdc_encoder->lcdc_clk); + + for (i = 0; i < ARRAY_SIZE(mdp4_lcdc_encoder->regs); i++) { + ret = regulator_disable(mdp4_lcdc_encoder->regs[i]); + if (ret) + dev_err(dev->dev, "failed to disable regulator: %d\n", ret); + } + + bs_set(mdp4_lcdc_encoder, 0); + } + + mdp4_lcdc_encoder->enabled = enabled; +} + +static bool mdp4_lcdc_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void mdp4_lcdc_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct mdp4_lcdc_encoder *mdp4_lcdc_encoder = + to_mdp4_lcdc_encoder(encoder); + struct mdp4_kms *mdp4_kms = get_kms(encoder); + uint32_t lcdc_hsync_skew, vsync_period, vsync_len, ctrl_pol; + uint32_t display_v_start, display_v_end; + uint32_t hsync_start_x, hsync_end_x; + + mode = adjusted_mode; + + DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", + mode->base.id, mode->name, + mode->vrefresh, mode->clock, + mode->hdisplay, mode->hsync_start, + mode->hsync_end, mode->htotal, + mode->vdisplay, mode->vsync_start, + mode->vsync_end, mode->vtotal, + mode->type, mode->flags); + + mdp4_lcdc_encoder->pixclock = mode->clock * 1000; + + DBG("pixclock=%lu", mdp4_lcdc_encoder->pixclock); + + ctrl_pol = 0; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + ctrl_pol |= MDP4_LCDC_CTRL_POLARITY_HSYNC_LOW; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + ctrl_pol |= MDP4_LCDC_CTRL_POLARITY_VSYNC_LOW; + /* probably need to get DATA_EN polarity from panel.. */ + + lcdc_hsync_skew = 0; /* get this from panel? */ + + hsync_start_x = (mode->htotal - mode->hsync_start); + hsync_end_x = mode->htotal - (mode->hsync_start - mode->hdisplay) - 1; + + vsync_period = mode->vtotal * mode->htotal; + vsync_len = (mode->vsync_end - mode->vsync_start) * mode->htotal; + display_v_start = (mode->vtotal - mode->vsync_start) * mode->htotal + lcdc_hsync_skew; + display_v_end = vsync_period - ((mode->vsync_start - mode->vdisplay) * mode->htotal) + lcdc_hsync_skew - 1; + + mdp4_write(mdp4_kms, REG_MDP4_LCDC_HSYNC_CTRL, + MDP4_LCDC_HSYNC_CTRL_PULSEW(mode->hsync_end - mode->hsync_start) | + MDP4_LCDC_HSYNC_CTRL_PERIOD(mode->htotal)); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_VSYNC_PERIOD, vsync_period); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_VSYNC_LEN, vsync_len); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_DISPLAY_HCTRL, + MDP4_LCDC_DISPLAY_HCTRL_START(hsync_start_x) | + MDP4_LCDC_DISPLAY_HCTRL_END(hsync_end_x)); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_DISPLAY_VSTART, display_v_start); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_DISPLAY_VEND, display_v_end); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_BORDER_CLR, 0); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_UNDERFLOW_CLR, + MDP4_LCDC_UNDERFLOW_CLR_ENABLE_RECOVERY | + MDP4_LCDC_UNDERFLOW_CLR_COLOR(0xff)); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_HSYNC_SKEW, lcdc_hsync_skew); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_CTRL_POLARITY, ctrl_pol); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_ACTIVE_HCTL, + MDP4_LCDC_ACTIVE_HCTL_START(0) | + MDP4_LCDC_ACTIVE_HCTL_END(0)); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_ACTIVE_VSTART, 0); + mdp4_write(mdp4_kms, REG_MDP4_LCDC_ACTIVE_VEND, 0); +} + +static void mdp4_lcdc_encoder_prepare(struct drm_encoder *encoder) +{ + mdp4_lcdc_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void mdp4_lcdc_encoder_commit(struct drm_encoder *encoder) +{ + /* TODO: hard-coded for 18bpp: */ + mdp4_crtc_set_config(encoder->crtc, + MDP4_DMA_CONFIG_R_BPC(BPC6) | + MDP4_DMA_CONFIG_G_BPC(BPC6) | + MDP4_DMA_CONFIG_B_BPC(BPC6) | + MDP4_DMA_CONFIG_PACK_ALIGN_MSB | + MDP4_DMA_CONFIG_PACK(0x21) | + MDP4_DMA_CONFIG_DEFLKR_EN | + MDP4_DMA_CONFIG_DITHER_EN); + mdp4_crtc_set_intf(encoder->crtc, INTF_LCDC_DTV, 0); + mdp4_lcdc_encoder_dpms(encoder, DRM_MODE_DPMS_ON); +} + +static const struct drm_encoder_helper_funcs mdp4_lcdc_encoder_helper_funcs = { + .dpms = mdp4_lcdc_encoder_dpms, + .mode_fixup = mdp4_lcdc_encoder_mode_fixup, + .mode_set = mdp4_lcdc_encoder_mode_set, + .prepare = mdp4_lcdc_encoder_prepare, + .commit = mdp4_lcdc_encoder_commit, +}; + +long mdp4_lcdc_round_pixclk(struct drm_encoder *encoder, unsigned long rate) +{ + struct mdp4_lcdc_encoder *mdp4_lcdc_encoder = + to_mdp4_lcdc_encoder(encoder); + return clk_round_rate(mdp4_lcdc_encoder->lcdc_clk, rate); +} + +/* initialize encoder */ +struct drm_encoder *mdp4_lcdc_encoder_init(struct drm_device *dev, + struct drm_panel *panel) +{ + struct drm_encoder *encoder = NULL; + struct mdp4_lcdc_encoder *mdp4_lcdc_encoder; + struct regulator *reg; + int ret; + + mdp4_lcdc_encoder = kzalloc(sizeof(*mdp4_lcdc_encoder), GFP_KERNEL); + if (!mdp4_lcdc_encoder) { + ret = -ENOMEM; + goto fail; + } + + mdp4_lcdc_encoder->panel = panel; + + encoder = &mdp4_lcdc_encoder->base; + + drm_encoder_init(dev, encoder, &mdp4_lcdc_encoder_funcs, + DRM_MODE_ENCODER_LVDS); + drm_encoder_helper_add(encoder, &mdp4_lcdc_encoder_helper_funcs); + + /* TODO: do we need different pll in other cases? */ + mdp4_lcdc_encoder->lcdc_clk = mpd4_lvds_pll_init(dev); + if (IS_ERR(mdp4_lcdc_encoder->lcdc_clk)) { + dev_err(dev->dev, "failed to get lvds_clk\n"); + ret = PTR_ERR(mdp4_lcdc_encoder->lcdc_clk); + goto fail; + } + + /* TODO: different regulators in other cases? */ + reg = devm_regulator_get(dev->dev, "lvds-vccs-3p3v"); + if (IS_ERR(reg)) { + ret = PTR_ERR(reg); + dev_err(dev->dev, "failed to get lvds-vccs-3p3v: %d\n", ret); + goto fail; + } + mdp4_lcdc_encoder->regs[0] = reg; + + reg = devm_regulator_get(dev->dev, "lvds-pll-vdda"); + if (IS_ERR(reg)) { + ret = PTR_ERR(reg); + dev_err(dev->dev, "failed to get lvds-pll-vdda: %d\n", ret); + goto fail; + } + mdp4_lcdc_encoder->regs[1] = reg; + + reg = devm_regulator_get(dev->dev, "lvds-vdda"); + if (IS_ERR(reg)) { + ret = PTR_ERR(reg); + dev_err(dev->dev, "failed to get lvds-vdda: %d\n", ret); + goto fail; + } + mdp4_lcdc_encoder->regs[2] = reg; + + bs_init(mdp4_lcdc_encoder); + + return encoder; + +fail: + if (encoder) + mdp4_lcdc_encoder_destroy(encoder); + + return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c new file mode 100644 index 0000000000000000000000000000000000000000..310034688c15a60a2a2d2d1364a1fb83bce8375b --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2014 Red Hat + * Author: Rob Clark + * Author: Vinay Simha + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include + +#include "mdp4_kms.h" + +struct mdp4_lvds_connector { + struct drm_connector base; + struct drm_encoder *encoder; + struct drm_panel *panel; +}; +#define to_mdp4_lvds_connector(x) container_of(x, struct mdp4_lvds_connector, base) + +static enum drm_connector_status mdp4_lvds_connector_detect( + struct drm_connector *connector, bool force) +{ + struct mdp4_lvds_connector *mdp4_lvds_connector = + to_mdp4_lvds_connector(connector); + + return mdp4_lvds_connector->panel ? + connector_status_connected : + connector_status_disconnected; +} + +static void mdp4_lvds_connector_destroy(struct drm_connector *connector) +{ + struct mdp4_lvds_connector *mdp4_lvds_connector = + to_mdp4_lvds_connector(connector); + struct drm_panel *panel = mdp4_lvds_connector->panel; + + if (panel) + drm_panel_detach(panel); + + drm_connector_unregister(connector); + drm_connector_cleanup(connector); + + kfree(mdp4_lvds_connector); +} + +static int mdp4_lvds_connector_get_modes(struct drm_connector *connector) +{ + struct mdp4_lvds_connector *mdp4_lvds_connector = + to_mdp4_lvds_connector(connector); + struct drm_panel *panel = mdp4_lvds_connector->panel; + int ret = 0; + + if (panel) + ret = panel->funcs->get_modes(panel); + + return ret; +} + +static int mdp4_lvds_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct mdp4_lvds_connector *mdp4_lvds_connector = + to_mdp4_lvds_connector(connector); + struct drm_encoder *encoder = mdp4_lvds_connector->encoder; + long actual, requested; + + requested = 1000 * mode->clock; + actual = mdp4_lcdc_round_pixclk(encoder, requested); + + DBG("requested=%ld, actual=%ld", requested, actual); + + if (actual != requested) + return MODE_CLOCK_RANGE; + + return MODE_OK; +} + +static struct drm_encoder * +mdp4_lvds_connector_best_encoder(struct drm_connector *connector) +{ + struct mdp4_lvds_connector *mdp4_lvds_connector = + to_mdp4_lvds_connector(connector); + return mdp4_lvds_connector->encoder; +} + +static const struct drm_connector_funcs mdp4_lvds_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = mdp4_lvds_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = mdp4_lvds_connector_destroy, +}; + +static const struct drm_connector_helper_funcs mdp4_lvds_connector_helper_funcs = { + .get_modes = mdp4_lvds_connector_get_modes, + .mode_valid = mdp4_lvds_connector_mode_valid, + .best_encoder = mdp4_lvds_connector_best_encoder, +}; + +/* initialize connector */ +struct drm_connector *mdp4_lvds_connector_init(struct drm_device *dev, + struct drm_panel *panel, struct drm_encoder *encoder) +{ + struct drm_connector *connector = NULL; + struct mdp4_lvds_connector *mdp4_lvds_connector; + int ret; + + mdp4_lvds_connector = kzalloc(sizeof(*mdp4_lvds_connector), GFP_KERNEL); + if (!mdp4_lvds_connector) { + ret = -ENOMEM; + goto fail; + } + + mdp4_lvds_connector->encoder = encoder; + mdp4_lvds_connector->panel = panel; + + connector = &mdp4_lvds_connector->base; + + drm_connector_init(dev, connector, &mdp4_lvds_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + drm_connector_helper_add(connector, &mdp4_lvds_connector_helper_funcs); + + connector->polled = 0; + + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + drm_connector_register(connector); + + drm_mode_connector_attach_encoder(connector, encoder); + + if (panel) + drm_panel_attach(panel, connector); + + return connector; + +fail: + if (connector) + mdp4_lvds_connector_destroy(connector); + + return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_pll.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_pll.c new file mode 100644 index 0000000000000000000000000000000000000000..ce4245971673ff3ae41018c445f958fab62e25eb --- /dev/null +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_pll.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2014 Red Hat + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include + +#include "mdp4_kms.h" + +struct mdp4_lvds_pll { + struct clk_hw pll_hw; + struct drm_device *dev; + unsigned long pixclk; +}; +#define to_mdp4_lvds_pll(x) container_of(x, struct mdp4_lvds_pll, pll_hw) + +static struct mdp4_kms *get_kms(struct mdp4_lvds_pll *lvds_pll) +{ + struct msm_drm_private *priv = lvds_pll->dev->dev_private; + return to_mdp4_kms(to_mdp_kms(priv->kms)); +} + +struct pll_rate { + unsigned long rate; + struct { + uint32_t val; + uint32_t reg; + } conf[32]; +}; + +/* NOTE: keep sorted highest freq to lowest: */ +static const struct pll_rate freqtbl[] = { + { 72000000, { + { 0x8f, REG_MDP4_LVDS_PHY_PLL_CTRL_1 }, + { 0x30, REG_MDP4_LVDS_PHY_PLL_CTRL_2 }, + { 0xc6, REG_MDP4_LVDS_PHY_PLL_CTRL_3 }, + { 0x10, REG_MDP4_LVDS_PHY_PLL_CTRL_5 }, + { 0x07, REG_MDP4_LVDS_PHY_PLL_CTRL_6 }, + { 0x62, REG_MDP4_LVDS_PHY_PLL_CTRL_7 }, + { 0x41, REG_MDP4_LVDS_PHY_PLL_CTRL_8 }, + { 0x0d, REG_MDP4_LVDS_PHY_PLL_CTRL_9 }, + { 0, 0 } } + }, +}; + +static const struct pll_rate *find_rate(unsigned long rate) +{ + int i; + for (i = 1; i < ARRAY_SIZE(freqtbl); i++) + if (rate > freqtbl[i].rate) + return &freqtbl[i-1]; + return &freqtbl[i-1]; +} + +static int mpd4_lvds_pll_enable(struct clk_hw *hw) +{ + struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw); + struct mdp4_kms *mdp4_kms = get_kms(lvds_pll); + const struct pll_rate *pll_rate = find_rate(lvds_pll->pixclk); + int i; + + DBG("pixclk=%lu (%lu)", lvds_pll->pixclk, pll_rate->rate); + + if (WARN_ON(!pll_rate)) + return -EINVAL; + + mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_PHY_RESET, 0x33); + + for (i = 0; pll_rate->conf[i].reg; i++) + mdp4_write(mdp4_kms, pll_rate->conf[i].reg, pll_rate->conf[i].val); + + mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_PLL_CTRL_0, 0x01); + + /* Wait until LVDS PLL is locked and ready */ + while (!mdp4_read(mdp4_kms, REG_MDP4_LVDS_PHY_PLL_LOCKED)) + cpu_relax(); + + return 0; +} + +static void mpd4_lvds_pll_disable(struct clk_hw *hw) +{ + struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw); + struct mdp4_kms *mdp4_kms = get_kms(lvds_pll); + + DBG(""); + + mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG0, 0x0); + mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_PLL_CTRL_0, 0x0); +} + +static unsigned long mpd4_lvds_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw); + return lvds_pll->pixclk; +} + +static long mpd4_lvds_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + const struct pll_rate *pll_rate = find_rate(rate); + return pll_rate->rate; +} + +static int mpd4_lvds_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw); + lvds_pll->pixclk = rate; + return 0; +} + + +static const struct clk_ops mpd4_lvds_pll_ops = { + .enable = mpd4_lvds_pll_enable, + .disable = mpd4_lvds_pll_disable, + .recalc_rate = mpd4_lvds_pll_recalc_rate, + .round_rate = mpd4_lvds_pll_round_rate, + .set_rate = mpd4_lvds_pll_set_rate, +}; + +static const char *mpd4_lvds_pll_parents[] = { + "pxo", +}; + +static struct clk_init_data pll_init = { + .name = "mpd4_lvds_pll", + .ops = &mpd4_lvds_pll_ops, + .parent_names = mpd4_lvds_pll_parents, + .num_parents = ARRAY_SIZE(mpd4_lvds_pll_parents), +}; + +struct clk *mpd4_lvds_pll_init(struct drm_device *dev) +{ + struct mdp4_lvds_pll *lvds_pll; + struct clk *clk; + int ret; + + lvds_pll = devm_kzalloc(dev->dev, sizeof(*lvds_pll), GFP_KERNEL); + if (!lvds_pll) { + ret = -ENOMEM; + goto fail; + } + + lvds_pll->dev = dev; + + lvds_pll->pll_hw.init = &pll_init; + clk = devm_clk_register(dev->dev, &lvds_pll->pll_hw); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto fail; + } + + return clk; + +fail: + return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index fcf95680413d5da99c86fed5a44c9c6386ba3350..b67ef59851250f6c442cf1dedfa0a88db5230ce9 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -280,7 +280,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags) dev->mode_config.max_height = 2048; dev->mode_config.funcs = &mode_config_funcs; - ret = drm_vblank_init(dev, 1); + ret = drm_vblank_init(dev, priv->num_crtcs); if (ret < 0) { dev_err(dev->dev, "failed to initialize vblank\n"); goto fail; @@ -315,39 +315,12 @@ static void load_gpu(struct drm_device *dev) { static DEFINE_MUTEX(init_lock); struct msm_drm_private *priv = dev->dev_private; - struct msm_gpu *gpu; mutex_lock(&init_lock); - if (priv->gpu) - goto out; - - gpu = a3xx_gpu_init(dev); - if (IS_ERR(gpu)) { - dev_warn(dev->dev, "failed to load a3xx gpu\n"); - gpu = NULL; - /* not fatal */ - } - - if (gpu) { - int ret; - mutex_lock(&dev->struct_mutex); - gpu->funcs->pm_resume(gpu); - mutex_unlock(&dev->struct_mutex); - ret = gpu->funcs->hw_init(gpu); - if (ret) { - dev_err(dev->dev, "gpu hw init failed: %d\n", ret); - gpu->funcs->destroy(gpu); - gpu = NULL; - } else { - /* give inactive pm a chance to kick in: */ - msm_gpu_retire(gpu); - } - } - - priv->gpu = gpu; + if (!priv->gpu) + priv->gpu = adreno_load_gpu(dev); -out: mutex_unlock(&init_lock); } @@ -836,6 +809,7 @@ static struct drm_driver msm_driver = { .open = msm_open, .preclose = msm_preclose, .lastclose = msm_lastclose, + .set_busid = drm_platform_set_busid, .irq_handler = msm_irq, .irq_preinstall = msm_irq_preinstall, .irq_postinstall = msm_irq_postinstall, @@ -1025,7 +999,7 @@ static int __init msm_drm_register(void) { DBG("init"); hdmi_register(); - a3xx_register(); + adreno_register(); return platform_driver_register(&msm_platform_driver); } @@ -1034,7 +1008,7 @@ static void __exit msm_drm_unregister(void) DBG("fini"); platform_driver_unregister(&msm_platform_driver); hdmi_unregister(); - a3xx_unregister(); + adreno_unregister(); } module_init(msm_drm_register); diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 8a2c5fd0893e07f824b46e2d2d7528e96eca2ace..67f9d0a2332c28b9983d7fe29000f907ffcd87f5 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -51,6 +51,7 @@ static inline struct device *msm_iommu_get_ctx(const char *ctx_name) #include #include #include +#include struct msm_kms; struct msm_gpu; @@ -170,7 +171,7 @@ struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj); void *msm_gem_prime_vmap(struct drm_gem_object *obj); void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev, - size_t size, struct sg_table *sg); + struct dma_buf_attachment *attach, struct sg_table *sg); int msm_gem_prime_pin(struct drm_gem_object *obj); void msm_gem_prime_unpin(struct drm_gem_object *obj); void *msm_gem_vaddr_locked(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/msm/msm_gem_prime.c b/drivers/gpu/drm/msm/msm_gem_prime.c index d48f9fc5129bfbd1b8d8b60842c7272f72eadc5f..ad772fe36115f69c47aaca1f1caa2c2356a8bd01 100644 --- a/drivers/gpu/drm/msm/msm_gem_prime.c +++ b/drivers/gpu/drm/msm/msm_gem_prime.c @@ -18,6 +18,7 @@ #include "msm_drv.h" #include "msm_gem.h" +#include struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj) { @@ -37,9 +38,9 @@ void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) } struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev, - size_t size, struct sg_table *sg) + struct dma_buf_attachment *attach, struct sg_table *sg) { - return msm_gem_import(dev, size, sg); + return msm_gem_import(dev, attach->dmabuf->size, sg); } int msm_gem_prime_pin(struct drm_gem_object *obj) diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 9b579b792840fb8d1c84bc1176ee7c992ab5e731..fd1e4b4a6d402f56b754427bc2066bc7de8e77ce 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -166,8 +166,8 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, const char *name, const char *ioname, const char *irqname, int ringsz); void msm_gpu_cleanup(struct msm_gpu *gpu); -struct msm_gpu *a3xx_gpu_init(struct drm_device *dev); -void __init a3xx_register(void); -void __exit a3xx_unregister(void); +struct msm_gpu *adreno_load_gpu(struct drm_device *dev); +void __init adreno_register(void); +void __exit adreno_unregister(void); #endif /* __MSM_GPU_H__ */ diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index f5d7f7ce4bc6b4b36d783cc1ec10d1680e5a5cd3..12c24c8abf7f54ab8c94af874b6220a931053d45 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -38,6 +38,7 @@ nouveau-y += core/subdev/bios/dcb.o nouveau-y += core/subdev/bios/disp.o nouveau-y += core/subdev/bios/dp.o nouveau-y += core/subdev/bios/extdev.o +nouveau-y += core/subdev/bios/fan.o nouveau-y += core/subdev/bios/gpio.o nouveau-y += core/subdev/bios/i2c.o nouveau-y += core/subdev/bios/init.o @@ -51,6 +52,8 @@ nouveau-y += core/subdev/bios/therm.o nouveau-y += core/subdev/bios/vmap.o nouveau-y += core/subdev/bios/volt.o nouveau-y += core/subdev/bios/xpio.o +nouveau-y += core/subdev/bios/M0205.o +nouveau-y += core/subdev/bios/M0209.o nouveau-y += core/subdev/bios/P0260.o nouveau-y += core/subdev/bus/hwsq.o nouveau-y += core/subdev/bus/nv04.o @@ -124,12 +127,17 @@ nouveau-y += core/subdev/fb/ramnvc0.o nouveau-y += core/subdev/fb/ramnve0.o nouveau-y += core/subdev/fb/ramgk20a.o nouveau-y += core/subdev/fb/ramgm107.o +nouveau-y += core/subdev/fb/sddr2.o nouveau-y += core/subdev/fb/sddr3.o nouveau-y += core/subdev/fb/gddr5.o +nouveau-y += core/subdev/fuse/base.o +nouveau-y += core/subdev/fuse/g80.o +nouveau-y += core/subdev/fuse/gf100.o +nouveau-y += core/subdev/fuse/gm107.o nouveau-y += core/subdev/gpio/base.o nouveau-y += core/subdev/gpio/nv10.o nouveau-y += core/subdev/gpio/nv50.o -nouveau-y += core/subdev/gpio/nv92.o +nouveau-y += core/subdev/gpio/nv94.o nouveau-y += core/subdev/gpio/nvd0.o nouveau-y += core/subdev/gpio/nve0.o nouveau-y += core/subdev/i2c/base.o @@ -190,6 +198,7 @@ nouveau-y += core/subdev/therm/nv50.o nouveau-y += core/subdev/therm/nv84.o nouveau-y += core/subdev/therm/nva3.o nouveau-y += core/subdev/therm/nvd0.o +nouveau-y += core/subdev/therm/gm107.o nouveau-y += core/subdev/timer/base.o nouveau-y += core/subdev/timer/nv04.o nouveau-y += core/subdev/timer/gk20a.o @@ -252,6 +261,7 @@ nouveau-y += core/engine/disp/hdanvd0.o nouveau-y += core/engine/disp/hdminv84.o nouveau-y += core/engine/disp/hdminva3.o nouveau-y += core/engine/disp/hdminvd0.o +nouveau-y += core/engine/disp/hdminve0.o nouveau-y += core/engine/disp/piornv50.o nouveau-y += core/engine/disp/sornv50.o nouveau-y += core/engine/disp/sornv94.o diff --git a/drivers/gpu/drm/nouveau/core/core/client.c b/drivers/gpu/drm/nouveau/core/core/client.c index 68bf06768123bb2d2fa92b0bf7777a28ad08f9c8..e962433294c317efb1e27f678d705092708c7e65 100644 --- a/drivers/gpu/drm/nouveau/core/core/client.c +++ b/drivers/gpu/drm/nouveau/core/core/client.c @@ -91,9 +91,10 @@ nvkm_client_notify_del(struct nouveau_client *client, int index) } int -nvkm_client_notify_new(struct nouveau_client *client, +nvkm_client_notify_new(struct nouveau_object *object, struct nvkm_event *event, void *data, u32 size) { + struct nouveau_client *client = nouveau_client(object); struct nvkm_client_notify *notify; union { struct nvif_notify_req_v0 v0; @@ -127,8 +128,8 @@ nvkm_client_notify_new(struct nouveau_client *client, } if (ret == 0) { - ret = nvkm_notify_init(event, nvkm_client_notify, false, - data, size, reply, ¬ify->n); + ret = nvkm_notify_init(object, event, nvkm_client_notify, + false, data, size, reply, ¬ify->n); if (ret == 0) { client->notify[index] = notify; notify->client = client; diff --git a/drivers/gpu/drm/nouveau/core/core/event.c b/drivers/gpu/drm/nouveau/core/core/event.c index 0540a48c5678e450f1fc6397948d17db59ccd560..ff2b434b3db480a47aa5b94515476714b2819693 100644 --- a/drivers/gpu/drm/nouveau/core/core/event.c +++ b/drivers/gpu/drm/nouveau/core/core/event.c @@ -20,7 +20,7 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#include +#include #include void diff --git a/drivers/gpu/drm/nouveau/core/core/gpuobj.c b/drivers/gpu/drm/nouveau/core/core/gpuobj.c index 560b2214cf1cc6fefed5d21dd5e0ded550df7666..daee877025029d62b2023a5fe018d72cf6491d9a 100644 --- a/drivers/gpu/drm/nouveau/core/core/gpuobj.c +++ b/drivers/gpu/drm/nouveau/core/core/gpuobj.c @@ -115,7 +115,7 @@ nouveau_gpuobj_create_(struct nouveau_object *parent, gpuobj->size = size; if (heap) { - ret = nouveau_mm_head(heap, 1, size, size, + ret = nouveau_mm_head(heap, 0, 1, size, size, max(align, (u32)1), &gpuobj->node); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/core/ioctl.c b/drivers/gpu/drm/nouveau/core/core/ioctl.c index f7e19bfb489c6920ae6ea6d4231b1affc8fab86c..692aa92dd850edabb7c53d1002e02e2b5cd1265d 100644 --- a/drivers/gpu/drm/nouveau/core/core/ioctl.c +++ b/drivers/gpu/drm/nouveau/core/core/ioctl.c @@ -349,7 +349,6 @@ nvkm_ioctl_unmap(struct nouveau_handle *handle, void *data, u32 size) static int nvkm_ioctl_ntfy_new(struct nouveau_handle *handle, void *data, u32 size) { - struct nouveau_client *client = nouveau_client(handle->object); struct nouveau_object *object = handle->object; struct nouveau_ofuncs *ofuncs = object->oclass->ofuncs; union { @@ -365,7 +364,7 @@ nvkm_ioctl_ntfy_new(struct nouveau_handle *handle, void *data, u32 size) if (ret = -ENODEV, ofuncs->ntfy) ret = ofuncs->ntfy(object, args->v0.event, &event); if (ret == 0) { - ret = nvkm_client_notify_new(client, event, data, size); + ret = nvkm_client_notify_new(object, event, data, size); if (ret >= 0) { args->v0.index = ret; ret = 0; diff --git a/drivers/gpu/drm/nouveau/core/core/mm.c b/drivers/gpu/drm/nouveau/core/core/mm.c index 7a4e0891c5f872e0dbda7dc5164b348aa59058c7..b4f5db66d5b545b58b351a5681f8509a5e7e71c1 100644 --- a/drivers/gpu/drm/nouveau/core/core/mm.c +++ b/drivers/gpu/drm/nouveau/core/core/mm.c @@ -28,6 +28,24 @@ #define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \ list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry) +static void +nouveau_mm_dump(struct nouveau_mm *mm, const char *header) +{ + struct nouveau_mm_node *node; + + printk(KERN_ERR "nouveau: %s\n", header); + printk(KERN_ERR "nouveau: node list:\n"); + list_for_each_entry(node, &mm->nodes, nl_entry) { + printk(KERN_ERR "nouveau: \t%08x %08x %d\n", + node->offset, node->length, node->type); + } + printk(KERN_ERR "nouveau: free list:\n"); + list_for_each_entry(node, &mm->free, fl_entry) { + printk(KERN_ERR "nouveau: \t%08x %08x %d\n", + node->offset, node->length, node->type); + } +} + void nouveau_mm_free(struct nouveau_mm *mm, struct nouveau_mm_node **pthis) { @@ -37,29 +55,29 @@ nouveau_mm_free(struct nouveau_mm *mm, struct nouveau_mm_node **pthis) struct nouveau_mm_node *prev = node(this, prev); struct nouveau_mm_node *next = node(this, next); - if (prev && prev->type == 0) { + if (prev && prev->type == NVKM_MM_TYPE_NONE) { prev->length += this->length; list_del(&this->nl_entry); kfree(this); this = prev; } - if (next && next->type == 0) { + if (next && next->type == NVKM_MM_TYPE_NONE) { next->offset = this->offset; next->length += this->length; - if (this->type == 0) + if (this->type == NVKM_MM_TYPE_NONE) list_del(&this->fl_entry); list_del(&this->nl_entry); kfree(this); this = NULL; } - if (this && this->type != 0) { + if (this && this->type != NVKM_MM_TYPE_NONE) { list_for_each_entry(prev, &mm->free, fl_entry) { if (this->offset < prev->offset) break; } list_add_tail(&this->fl_entry, &prev->fl_entry); - this->type = 0; + this->type = NVKM_MM_TYPE_NONE; } } @@ -80,27 +98,32 @@ region_head(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size) b->offset = a->offset; b->length = size; + b->heap = a->heap; b->type = a->type; a->offset += size; a->length -= size; list_add_tail(&b->nl_entry, &a->nl_entry); - if (b->type == 0) + if (b->type == NVKM_MM_TYPE_NONE) list_add_tail(&b->fl_entry, &a->fl_entry); return b; } int -nouveau_mm_head(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min, - u32 align, struct nouveau_mm_node **pnode) +nouveau_mm_head(struct nouveau_mm *mm, u8 heap, u8 type, u32 size_max, + u32 size_min, u32 align, struct nouveau_mm_node **pnode) { struct nouveau_mm_node *prev, *this, *next; u32 mask = align - 1; u32 splitoff; u32 s, e; - BUG_ON(!type); + BUG_ON(type == NVKM_MM_TYPE_NONE || type == NVKM_MM_TYPE_HOLE); list_for_each_entry(this, &mm->free, fl_entry) { + if (unlikely(heap != NVKM_MM_HEAP_ANY)) { + if (this->heap != heap) + continue; + } e = this->offset + this->length; s = this->offset; @@ -149,27 +172,32 @@ region_tail(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size) a->length -= size; b->offset = a->offset + a->length; b->length = size; + b->heap = a->heap; b->type = a->type; list_add(&b->nl_entry, &a->nl_entry); - if (b->type == 0) + if (b->type == NVKM_MM_TYPE_NONE) list_add(&b->fl_entry, &a->fl_entry); return b; } int -nouveau_mm_tail(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min, - u32 align, struct nouveau_mm_node **pnode) +nouveau_mm_tail(struct nouveau_mm *mm, u8 heap, u8 type, u32 size_max, + u32 size_min, u32 align, struct nouveau_mm_node **pnode) { struct nouveau_mm_node *prev, *this, *next; u32 mask = align - 1; - BUG_ON(!type); + BUG_ON(type == NVKM_MM_TYPE_NONE || type == NVKM_MM_TYPE_HOLE); list_for_each_entry_reverse(this, &mm->free, fl_entry) { u32 e = this->offset + this->length; u32 s = this->offset; u32 c = 0, a; + if (unlikely(heap != NVKM_MM_HEAP_ANY)) { + if (this->heap != heap) + continue; + } prev = node(this, prev); if (prev && prev->type != type) @@ -209,9 +237,23 @@ nouveau_mm_tail(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min, int nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block) { - struct nouveau_mm_node *node; + struct nouveau_mm_node *node, *prev; + u32 next; - if (block) { + if (nouveau_mm_initialised(mm)) { + prev = list_last_entry(&mm->nodes, typeof(*node), nl_entry); + next = prev->offset + prev->length; + if (next != offset) { + BUG_ON(next > offset); + if (!(node = kzalloc(sizeof(*node), GFP_KERNEL))) + return -ENOMEM; + node->type = NVKM_MM_TYPE_HOLE; + node->offset = next; + node->length = offset - next; + list_add_tail(&node->nl_entry, &mm->nodes); + } + BUG_ON(block != mm->block_size); + } else { INIT_LIST_HEAD(&mm->nodes); INIT_LIST_HEAD(&mm->free); mm->block_size = block; @@ -230,25 +272,32 @@ nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block) list_add_tail(&node->nl_entry, &mm->nodes); list_add_tail(&node->fl_entry, &mm->free); - mm->heap_nodes++; + node->heap = ++mm->heap_nodes; return 0; } int nouveau_mm_fini(struct nouveau_mm *mm) { - if (nouveau_mm_initialised(mm)) { - struct nouveau_mm_node *node, *heap = - list_first_entry(&mm->nodes, typeof(*heap), nl_entry); - int nodes = 0; + struct nouveau_mm_node *node, *temp; + int nodes = 0; - list_for_each_entry(node, &mm->nodes, nl_entry) { - if (WARN_ON(nodes++ == mm->heap_nodes)) + if (!nouveau_mm_initialised(mm)) + return 0; + + list_for_each_entry(node, &mm->nodes, nl_entry) { + if (node->type != NVKM_MM_TYPE_HOLE) { + if (++nodes > mm->heap_nodes) { + nouveau_mm_dump(mm, "mm not clean!"); return -EBUSY; + } } - - kfree(heap); } + list_for_each_entry_safe(node, temp, &mm->nodes, nl_entry) { + list_del(&node->nl_entry); + kfree(node); + } + mm->heap_nodes = 0; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/core/notify.c b/drivers/gpu/drm/nouveau/core/core/notify.c index 76adb81bdea21d2fc2c6f22fd30686a8395bfd03..d1bcde55e9d734df7573366a21f87e8b40a5f18b 100644 --- a/drivers/gpu/drm/nouveau/core/core/notify.c +++ b/drivers/gpu/drm/nouveau/core/core/notify.c @@ -134,14 +134,15 @@ nvkm_notify_fini(struct nvkm_notify *notify) } int -nvkm_notify_init(struct nvkm_event *event, int (*func)(struct nvkm_notify *), - bool work, void *data, u32 size, u32 reply, +nvkm_notify_init(struct nouveau_object *object, struct nvkm_event *event, + int (*func)(struct nvkm_notify *), bool work, + void *data, u32 size, u32 reply, struct nvkm_notify *notify) { unsigned long flags; int ret = -ENODEV; if ((notify->event = event), event->refs) { - ret = event->func->ctor(data, size, notify); + ret = event->func->ctor(object, data, size, notify); if (ret == 0 && (ret = -EINVAL, notify->size == reply)) { notify->flags = 0; notify->block = 1; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/base.c b/drivers/gpu/drm/nouveau/core/engine/device/base.c index 8928f7981d4ada8eb4a7758878504e61b5a668ca..0ef5a5713182bc5e8d05a45ad8670672286d945b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/base.c @@ -505,7 +505,8 @@ nouveau_device_sclass[] = { }; static int -nouveau_device_event_ctor(void *data, u32 size, struct nvkm_notify *notify) +nouveau_device_event_ctor(struct nouveau_object *object, void *data, u32 size, + struct nvkm_notify *notify) { if (!WARN_ON(size != 0)) { notify->size = 0; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/gm100.c b/drivers/gpu/drm/nouveau/core/engine/device/gm100.c index 377ec0b8851e8a2d8606b1f461dfd302fc6b99c0..6295668e29a5897a45e08d2ce09efac9c5aac721 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/gm100.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/gm100.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -62,10 +63,9 @@ gm100_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gm107_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; -#if 0 - device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; -#endif + device->oclass[NVDEV_SUBDEV_THERM ] = &gm107_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; device->oclass[NVDEV_SUBDEV_DEVINIT] = gm107_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass; @@ -77,8 +77,9 @@ gm100_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass; device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass; -#if 0 device->oclass[NVDEV_SUBDEV_PWR ] = nv108_pwr_oclass; + +#if 0 device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; #endif device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvd0_dmaeng_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv50.c b/drivers/gpu/drm/nouveau/core/engine/device/nv50.c index 932f84fae4591c88c46164875afe76785a60476d..96f568d1321bc8d44abca71d98956a8dc3b01799 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nv50.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv50_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -87,6 +89,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -115,6 +118,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -141,8 +145,9 @@ nv50_identify(struct nouveau_device *device) case 0x92: device->cname = "G92"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -169,8 +174,9 @@ nv50_identify(struct nouveau_device *device) case 0x94: device->cname = "G94"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -197,8 +203,9 @@ nv50_identify(struct nouveau_device *device) case 0x96: device->cname = "G96"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -225,8 +232,9 @@ nv50_identify(struct nouveau_device *device) case 0x98: device->cname = "G98"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -253,8 +261,9 @@ nv50_identify(struct nouveau_device *device) case 0xa0: device->cname = "G200"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -281,8 +290,9 @@ nv50_identify(struct nouveau_device *device) case 0xaa: device->cname = "MCP77/MCP78"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nvaa_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -309,8 +319,9 @@ nv50_identify(struct nouveau_device *device) case 0xac: device->cname = "MCP79/MCP7A"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = nvaa_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -337,8 +348,9 @@ nv50_identify(struct nouveau_device *device) case 0xa3: device->cname = "GT215"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -367,8 +379,9 @@ nv50_identify(struct nouveau_device *device) case 0xa5: device->cname = "GT216"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -396,8 +409,9 @@ nv50_identify(struct nouveau_device *device) case 0xa8: device->cname = "GT218"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -425,8 +439,9 @@ nv50_identify(struct nouveau_device *device) case 0xaf: device->cname = "MCP89"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c index b4a2917ce555705b24152714935c6edb0245e681..cd05677ad4b7a128a087f4b611f73b5ab4978006 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -60,8 +61,9 @@ nvc0_identify(struct nouveau_device *device) case 0xc0: device->cname = "GF100"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -92,8 +94,9 @@ nvc0_identify(struct nouveau_device *device) case 0xc4: device->cname = "GF104"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -124,8 +127,9 @@ nvc0_identify(struct nouveau_device *device) case 0xc3: device->cname = "GF106"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -155,8 +159,9 @@ nvc0_identify(struct nouveau_device *device) case 0xce: device->cname = "GF114"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -187,8 +192,9 @@ nvc0_identify(struct nouveau_device *device) case 0xcf: device->cname = "GF116"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -219,8 +225,9 @@ nvc0_identify(struct nouveau_device *device) case 0xc1: device->cname = "GF108"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -250,8 +257,9 @@ nvc0_identify(struct nouveau_device *device) case 0xc8: device->cname = "GF110"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; - device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -284,6 +292,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nvd0_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -315,6 +324,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nvd0_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = gf117_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c index cdf9147f32a1e80df5c5e58fa840ce311fa54819..b1b2e484ecfabb1a51426a9737fa3d05a014c30c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -95,6 +97,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -128,6 +131,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -161,6 +165,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &gk20a_clock_oclass; device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &gk20a_timer_oclass; device->oclass[NVDEV_SUBDEV_FB ] = gk20a_fb_oclass; device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass; @@ -180,6 +185,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -213,6 +219,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; @@ -246,6 +253,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/base.c b/drivers/gpu/drm/nouveau/core/engine/disp/base.c index 22d55f6cde5011f34c19bc938066d1306fcbd7e7..64b84667f3a5e9ebbf9f57ae2b1c6c4f76997d5f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/base.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/base.c @@ -32,7 +32,8 @@ #include "conn.h" int -nouveau_disp_vblank_ctor(void *data, u32 size, struct nvkm_notify *notify) +nouveau_disp_vblank_ctor(struct nouveau_object *object, void *data, u32 size, + struct nvkm_notify *notify) { struct nouveau_disp *disp = container_of(notify->event, typeof(*disp), vblank); @@ -61,7 +62,8 @@ nouveau_disp_vblank(struct nouveau_disp *disp, int head) } static int -nouveau_disp_hpd_ctor(void *data, u32 size, struct nvkm_notify *notify) +nouveau_disp_hpd_ctor(struct nouveau_object *object, void *data, u32 size, + struct nvkm_notify *notify) { struct nouveau_disp *disp = container_of(notify->event, typeof(*disp), hpd); diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/conn.c b/drivers/gpu/drm/nouveau/core/engine/disp/conn.c index 3d1070228977eb86d3434a7282f7800452e0b6cb..1496b567dd4aab9e1978bda882b1bf3ab3802da0 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/conn.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/conn.c @@ -126,8 +126,8 @@ nvkm_connector_create_(struct nouveau_object *parent, return 0; } - ret = nvkm_notify_init(&gpio->event, nvkm_connector_hpd, true, - &(struct nvkm_gpio_ntfy_req) { + ret = nvkm_notify_init(NULL, &gpio->event, nvkm_connector_hpd, + true, &(struct nvkm_gpio_ntfy_req) { .mask = NVKM_GPIO_TOGGLED, .line = func.line, }, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c b/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c index d54da8b5f87ec97f5587bde7447ab2fbbbe5f918..b3df3fe2dc094b6dbebdc861c94450ef48ddf3e2 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c @@ -68,6 +68,10 @@ gm107_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + ret = nvkm_event_init(&nvd0_disp_chan_uevent, 1, 17, &priv->uevent); + if (ret) + return ret; + nv_engine(priv)->sclass = gm107_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nvd0_disp_intr; @@ -80,7 +84,7 @@ gm107_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->dac.sense = nv50_dac_sense; priv->sor.power = nv50_sor_power; priv->sor.hda_eld = nvd0_hda_eld; - priv->sor.hdmi = nvd0_hdmi_ctrl; + priv->sor.hdmi = nve0_hdmi_ctrl; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/hdanva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/hdanva3.c index 8b4e06abe5333230768ef047b58c3855a6cca305..fe9ef5894dd49e5fd93722cab01114f8c4b769ab 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/hdanva3.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/hdanva3.c @@ -26,6 +26,8 @@ #include #include +#include + #include "nv50.h" int @@ -46,16 +48,21 @@ nva3_hda_eld(NV50_DISP_MTHD_V1) return ret; if (size && args->v0.data[0]) { + if (outp->info.type == DCB_OUTPUT_DP) { + nv_mask(priv, 0x61c1e0 + soff, 0x8000000d, 0x80000001); + nv_wait(priv, 0x61c1e0 + soff, 0x80000000, 0x00000000); + } for (i = 0; i < size; i++) nv_wr32(priv, 0x61c440 + soff, (i << 8) | args->v0.data[0]); for (; i < 0x60; i++) nv_wr32(priv, 0x61c440 + soff, (i << 8)); nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000003); - } else - if (size) { - nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000001); } else { - nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000000); + if (outp->info.type == DCB_OUTPUT_DP) { + nv_mask(priv, 0x61c1e0 + soff, 0x80000001, 0x80000000); + nv_wait(priv, 0x61c1e0 + soff, 0x80000000, 0x00000000); + } + nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000000 | !!size); } return 0; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/hdanvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/hdanvd0.c index baf558fc12fb91dac6e2fd8d7a67a28084a7f926..1d4e8432d8579d54ba0934f8af591e4d572f3218 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/hdanvd0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/hdanvd0.c @@ -26,10 +26,7 @@ #include #include -#include -#include -#include -#include +#include #include "nv50.h" @@ -40,6 +37,7 @@ nvd0_hda_eld(NV50_DISP_MTHD_V1) struct nv50_disp_sor_hda_eld_v0 v0; } *args = data; const u32 soff = outp->or * 0x030; + const u32 hoff = head * 0x800; int ret, i; nv_ioctl(object, "disp sor hda eld size %d\n", size); @@ -51,16 +49,22 @@ nvd0_hda_eld(NV50_DISP_MTHD_V1) return ret; if (size && args->v0.data[0]) { + if (outp->info.type == DCB_OUTPUT_DP) { + nv_mask(priv, 0x616618 + hoff, 0x8000000c, 0x80000001); + nv_wait(priv, 0x616618 + hoff, 0x80000000, 0x00000000); + } + nv_mask(priv, 0x616548 + hoff, 0x00000070, 0x00000000); for (i = 0; i < size; i++) nv_wr32(priv, 0x10ec00 + soff, (i << 8) | args->v0.data[i]); for (; i < 0x60; i++) nv_wr32(priv, 0x10ec00 + soff, (i << 8)); nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000003); - } else - if (size) { - nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000001); } else { - nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000000); + if (outp->info.type == DCB_OUTPUT_DP) { + nv_mask(priv, 0x616618 + hoff, 0x80000001, 0x80000000); + nv_wait(priv, 0x616618 + hoff, 0x80000000, 0x00000000); + } + nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000000 | !!size); } return 0; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/hdminvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/hdminvd0.c index 3106d295b48d978028f62259a169935cdce0e012..bac4fc4570f0e3731f3a09ef1bc0869c015023c8 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/hdminvd0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/hdminvd0.c @@ -75,8 +75,5 @@ nvd0_hdmi_ctrl(NV50_DISP_MTHD_V1) /* HDMI_CTRL */ nv_mask(priv, 0x616798 + hoff, 0x401f007f, ctrl); - - /* NFI, audio doesn't work without it though.. */ - nv_mask(priv, 0x616548 + hoff, 0x00000070, 0x00000000); return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/hdminve0.c b/drivers/gpu/drm/nouveau/core/engine/disp/hdminve0.c new file mode 100644 index 0000000000000000000000000000000000000000..528d14ec2f7f5a7c0dfd4da2d606a5fe21b1d83f --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/hdminve0.c @@ -0,0 +1,83 @@ +/* + * Copyright 2014 Red Hat Inc. + * + * 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. + * + * Authors: Ben Skeggs + */ + +#include +#include +#include + +#include "nv50.h" + +int +nve0_hdmi_ctrl(NV50_DISP_MTHD_V1) +{ + const u32 hoff = (head * 0x800); + const u32 hdmi = (head * 0x400); + union { + struct nv50_disp_sor_hdmi_pwr_v0 v0; + } *args = data; + u32 ctrl; + int ret; + + nv_ioctl(object, "disp sor hdmi ctrl size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "disp sor hdmi ctrl vers %d state %d " + "max_ac_packet %d rekey %d\n", + args->v0.version, args->v0.state, + args->v0.max_ac_packet, args->v0.rekey); + if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) + return -EINVAL; + ctrl = 0x40000000 * !!args->v0.state; + ctrl |= args->v0.max_ac_packet << 16; + ctrl |= args->v0.rekey; + } else + return ret; + + if (!(ctrl & 0x40000000)) { + nv_mask(priv, 0x616798 + hoff, 0x40000000, 0x00000000); + nv_mask(priv, 0x6900c0 + hdmi, 0x00000001, 0x00000000); + nv_mask(priv, 0x690000 + hdmi, 0x00000001, 0x00000000); + return 0; + } + + /* AVI InfoFrame */ + nv_mask(priv, 0x690000 + hdmi, 0x00000001, 0x00000000); + nv_wr32(priv, 0x690008 + hdmi, 0x000d0282); + nv_wr32(priv, 0x69000c + hdmi, 0x0000006f); + nv_wr32(priv, 0x690010 + hdmi, 0x00000000); + nv_wr32(priv, 0x690014 + hdmi, 0x00000000); + nv_wr32(priv, 0x690018 + hdmi, 0x00000000); + nv_mask(priv, 0x690000 + hdmi, 0x00000001, 0x00000001); + + /* ??? InfoFrame? */ + nv_mask(priv, 0x6900c0 + hdmi, 0x00000001, 0x00000000); + nv_wr32(priv, 0x6900cc + hdmi, 0x00000010); + nv_mask(priv, 0x6900c0 + hdmi, 0x00000001, 0x00000001); + + /* ??? */ + nv_wr32(priv, 0x690080 + hdmi, 0x82000000); + + /* HDMI_CTRL */ + nv_mask(priv, 0x616798 + hoff, 0x401f007f, ctrl); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index f8cbb512132fdcd224f6dda223689de4a4bb4350..2df3a937037de7c55d94f345bcdd8f2eeb88576e 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -82,6 +83,71 @@ nv50_disp_chan_destroy(struct nv50_disp_chan *chan) nouveau_namedb_destroy(&chan->base); } +static void +nv50_disp_chan_uevent_fini(struct nvkm_event *event, int type, int index) +{ + struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent); + nv_mask(priv, 0x610028, 0x00000001 << index, 0x00000000 << index); +} + +static void +nv50_disp_chan_uevent_init(struct nvkm_event *event, int types, int index) +{ + struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent); + nv_mask(priv, 0x610028, 0x00000001 << index, 0x00000001 << index); +} + +void +nv50_disp_chan_uevent_send(struct nv50_disp_priv *priv, int chid) +{ + struct nvif_notify_uevent_rep { + } rep; + + nvkm_event_send(&priv->uevent, 1, chid, &rep, sizeof(rep)); +} + +int +nv50_disp_chan_uevent_ctor(struct nouveau_object *object, void *data, u32 size, + struct nvkm_notify *notify) +{ + struct nv50_disp_dmac *dmac = (void *)object; + union { + struct nvif_notify_uevent_req none; + } *args = data; + int ret; + + if (nvif_unvers(args->none)) { + notify->size = sizeof(struct nvif_notify_uevent_rep); + notify->types = 1; + notify->index = dmac->base.chid; + return 0; + } + + return ret; +} + +const struct nvkm_event_func +nv50_disp_chan_uevent = { + .ctor = nv50_disp_chan_uevent_ctor, + .init = nv50_disp_chan_uevent_init, + .fini = nv50_disp_chan_uevent_fini, +}; + +int +nv50_disp_chan_ntfy(struct nouveau_object *object, u32 type, + struct nvkm_event **pevent) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + switch (type) { + case NV50_DISP_CORE_CHANNEL_DMA_V0_NTFY_UEVENT: + *pevent = &priv->uevent; + return 0; + default: + break; + } + return -EINVAL; +} + int nv50_disp_chan_map(struct nouveau_object *object, u64 *addr, u32 *size) { @@ -195,7 +261,7 @@ nv50_disp_dmac_init(struct nouveau_object *object) return ret; /* enable error reporting */ - nv_mask(priv, 0x610028, 0x00010001 << chid, 0x00010001 << chid); + nv_mask(priv, 0x610028, 0x00010000 << chid, 0x00010000 << chid); /* initialise channel for dma command submission */ nv_wr32(priv, 0x610204 + (chid * 0x0010), dmac->push); @@ -232,7 +298,7 @@ nv50_disp_dmac_fini(struct nouveau_object *object, bool suspend) return -EBUSY; } - /* disable error reporting */ + /* disable error reporting and completion notifications */ nv_mask(priv, 0x610028, 0x00010001 << chid, 0x00000000 << chid); return nv50_disp_chan_fini(&dmac->base, suspend); @@ -454,7 +520,7 @@ nv50_disp_mast_init(struct nouveau_object *object) return ret; /* enable error reporting */ - nv_mask(priv, 0x610028, 0x00010001, 0x00010001); + nv_mask(priv, 0x610028, 0x00010000, 0x00010000); /* attempt to unstick channel from some unknown state */ if ((nv_rd32(priv, 0x610200) & 0x009f0000) == 0x00020000) @@ -494,7 +560,7 @@ nv50_disp_mast_fini(struct nouveau_object *object, bool suspend) return -EBUSY; } - /* disable error reporting */ + /* disable error reporting and completion notifications */ nv_mask(priv, 0x610028, 0x00010001, 0x00000000); return nv50_disp_chan_fini(&mast->base, suspend); @@ -507,6 +573,7 @@ nv50_disp_mast_ofuncs = { .base.init = nv50_disp_mast_init, .base.fini = nv50_disp_mast_fini, .base.map = nv50_disp_chan_map, + .base.ntfy = nv50_disp_chan_ntfy, .base.rd32 = nv50_disp_chan_rd32, .base.wr32 = nv50_disp_chan_wr32, .chid = 0, @@ -607,6 +674,7 @@ nv50_disp_sync_ofuncs = { .base.dtor = nv50_disp_dmac_dtor, .base.init = nv50_disp_dmac_init, .base.fini = nv50_disp_dmac_fini, + .base.ntfy = nv50_disp_chan_ntfy, .base.map = nv50_disp_chan_map, .base.rd32 = nv50_disp_chan_rd32, .base.wr32 = nv50_disp_chan_wr32, @@ -696,6 +764,7 @@ nv50_disp_ovly_ofuncs = { .base.dtor = nv50_disp_dmac_dtor, .base.init = nv50_disp_dmac_init, .base.fini = nv50_disp_dmac_fini, + .base.ntfy = nv50_disp_chan_ntfy, .base.map = nv50_disp_chan_map, .base.rd32 = nv50_disp_chan_rd32, .base.wr32 = nv50_disp_chan_wr32, @@ -813,6 +882,7 @@ nv50_disp_oimm_ofuncs = { .base.dtor = nv50_disp_pioc_dtor, .base.init = nv50_disp_pioc_init, .base.fini = nv50_disp_pioc_fini, + .base.ntfy = nv50_disp_chan_ntfy, .base.map = nv50_disp_chan_map, .base.rd32 = nv50_disp_chan_rd32, .base.wr32 = nv50_disp_chan_wr32, @@ -860,6 +930,7 @@ nv50_disp_curs_ofuncs = { .base.dtor = nv50_disp_pioc_dtor, .base.init = nv50_disp_pioc_init, .base.fini = nv50_disp_pioc_fini, + .base.ntfy = nv50_disp_chan_ntfy, .base.map = nv50_disp_chan_map, .base.rd32 = nv50_disp_chan_rd32, .base.wr32 = nv50_disp_chan_wr32, @@ -1559,7 +1630,7 @@ nv50_disp_intr_unk20_1(struct nv50_disp_priv *priv, int head) } static void -nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv, +nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv, int head, struct dcb_output *outp, u32 pclk) { const int link = !(outp->sorconf.link & 1); @@ -1568,24 +1639,36 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv, const u32 loff = (link * 0x080) + soff; const u32 ctrl = nv_rd32(priv, 0x610794 + (or * 8)); const u32 symbol = 100000; - u32 dpctrl = nv_rd32(priv, 0x61c10c + loff) & 0x0000f0000; + const s32 vactive = nv_rd32(priv, 0x610af8 + (head * 0x540)) & 0xffff; + const s32 vblanke = nv_rd32(priv, 0x610ae8 + (head * 0x540)) & 0xffff; + const s32 vblanks = nv_rd32(priv, 0x610af0 + (head * 0x540)) & 0xffff; + u32 dpctrl = nv_rd32(priv, 0x61c10c + loff); u32 clksor = nv_rd32(priv, 0x614300 + soff); int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; int TU, VTUi, VTUf, VTUa; u64 link_data_rate, link_ratio, unk; u32 best_diff = 64 * symbol; u32 link_nr, link_bw, bits; - - /* calculate packed data rate for each lane */ - if (dpctrl > 0x00030000) link_nr = 4; - else if (dpctrl > 0x00010000) link_nr = 2; - else link_nr = 1; - - if (clksor & 0x000c0000) - link_bw = 270000; - else - link_bw = 162000; - + u64 value; + + link_bw = (clksor & 0x000c0000) ? 270000 : 162000; + link_nr = hweight32(dpctrl & 0x000f0000); + + /* symbols/hblank - algorithm taken from comments in tegra driver */ + value = vblanke + vactive - vblanks - 7; + value = value * link_bw; + do_div(value, pclk); + value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr); + nv_mask(priv, 0x61c1e8 + soff, 0x0000ffff, value); + + /* symbols/vblank - algorithm taken from comments in tegra driver */ + value = vblanks - vblanke - 25; + value = value * link_bw; + do_div(value, pclk); + value = value - ((36 / link_nr) + 3) - 1; + nv_mask(priv, 0x61c1ec + soff, 0x00ffffff, value); + + /* watermark / activesym */ if ((ctrl & 0xf0000) == 0x60000) bits = 30; else if ((ctrl & 0xf0000) == 0x50000) bits = 24; else bits = 18; @@ -1731,7 +1814,7 @@ nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head) } else if (!outp->info.location) { if (outp->info.type == DCB_OUTPUT_DP) - nv50_disp_intr_unk20_2_dp(priv, &outp->info, pclk); + nv50_disp_intr_unk20_2_dp(priv, head, &outp->info, pclk); oreg = 0x614300 + (ffs(outp->info.or) - 1) * 0x800; oval = (conf & 0x0100) ? 0x00000101 : 0x00000000; hval = 0x00000000; @@ -1847,6 +1930,12 @@ nv50_disp_intr(struct nouveau_subdev *subdev) intr0 &= ~(0x00010000 << chid); } + while (intr0 & 0x0000001f) { + u32 chid = __ffs(intr0 & 0x0000001f); + nv50_disp_chan_uevent_send(priv, chid); + intr0 &= ~(0x00000001 << chid); + } + if (intr1 & 0x00000004) { nouveau_disp_vblank(&priv->base, 0); nv_wr32(priv, 0x610024, 0x00000004); @@ -1881,6 +1970,10 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent); + if (ret) + return ret; + nv_engine(priv)->sclass = nv50_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nv50_disp_intr; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h index 8ab14461f70c645116b4bf82e36cd8afda36fad4..5279feefec062210706e087b315e802a84648913 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h @@ -26,6 +26,8 @@ struct nv50_disp_priv { struct work_struct supervisor; u32 super; + struct nvkm_event uevent; + struct { int nr; } head; @@ -75,6 +77,7 @@ int nvd0_hda_eld(NV50_DISP_MTHD_V1); int nv84_hdmi_ctrl(NV50_DISP_MTHD_V1); int nva3_hdmi_ctrl(NV50_DISP_MTHD_V1); int nvd0_hdmi_ctrl(NV50_DISP_MTHD_V1); +int nve0_hdmi_ctrl(NV50_DISP_MTHD_V1); int nv50_sor_power(NV50_DISP_MTHD_V1); @@ -116,9 +119,16 @@ struct nv50_disp_chan { int chid; }; +int nv50_disp_chan_ntfy(struct nouveau_object *, u32, struct nvkm_event **); int nv50_disp_chan_map(struct nouveau_object *, u64 *, u32 *); u32 nv50_disp_chan_rd32(struct nouveau_object *, u64); void nv50_disp_chan_wr32(struct nouveau_object *, u64, u32); +extern const struct nvkm_event_func nv50_disp_chan_uevent; +int nv50_disp_chan_uevent_ctor(struct nouveau_object *, void *, u32, + struct nvkm_notify *); +void nv50_disp_chan_uevent_send(struct nv50_disp_priv *, int); + +extern const struct nvkm_event_func nvd0_disp_chan_uevent; #define nv50_disp_chan_init(a) \ nouveau_namedb_init(&(a)->base) diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c index 788ced1b6182f8c5cd925ed402cd6c85f772eb66..d36284715b2ab0f20ae26bce925fe05b3b1bea67 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c @@ -236,6 +236,10 @@ nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent); + if (ret) + return ret; + nv_engine(priv)->sclass = nv84_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nv50_disp_intr; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c index fa79de906eaefd1dd39e90e9e2bc0d8994c6ebca..a117064002b1413a499a5a644ebd0c439abcdf75 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c @@ -95,6 +95,10 @@ nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent); + if (ret) + return ret; + nv_engine(priv)->sclass = nv94_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nv50_disp_intr; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c index 7af15f5d48dc2c08a7ecc0c694ab1f7c04a0a524..c67e68aadd45987ae1f5dc6fb0a48a73102b1fcc 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c @@ -112,6 +112,10 @@ nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent); + if (ret) + return ret; + nv_engine(priv)->sclass = nva0_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nv50_disp_intr; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c index 6bd39448f8dab91d621a09cc22b99a9c8b9ab591..22969f355aae66da0102bf722a87cbff81b529fc 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c @@ -67,6 +67,10 @@ nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent); + if (ret) + return ret; + nv_engine(priv)->sclass = nva3_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nv50_disp_intr; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c index a4bb3c774ee1f6a853289003ab98a73cc2a78c5a..747e64bb9c06c935e1318a57ac7d8b879b473366 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c @@ -42,6 +42,31 @@ #include "nv50.h" +/******************************************************************************* + * EVO channel base class + ******************************************************************************/ + +static void +nvd0_disp_chan_uevent_fini(struct nvkm_event *event, int type, int index) +{ + struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent); + nv_mask(priv, 0x610090, 0x00000001 << index, 0x00000000 << index); +} + +static void +nvd0_disp_chan_uevent_init(struct nvkm_event *event, int types, int index) +{ + struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent); + nv_mask(priv, 0x610090, 0x00000001 << index, 0x00000001 << index); +} + +const struct nvkm_event_func +nvd0_disp_chan_uevent = { + .ctor = nv50_disp_chan_uevent_ctor, + .init = nvd0_disp_chan_uevent_init, + .fini = nvd0_disp_chan_uevent_fini, +}; + /******************************************************************************* * EVO DMA channel base class ******************************************************************************/ @@ -77,7 +102,6 @@ nvd0_disp_dmac_init(struct nouveau_object *object) return ret; /* enable error reporting */ - nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid); nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid); /* initialise channel for dma command submission */ @@ -115,7 +139,7 @@ nvd0_disp_dmac_fini(struct nouveau_object *object, bool suspend) return -EBUSY; } - /* disable error reporting */ + /* disable error reporting and completion notification */ nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000); nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000); @@ -278,7 +302,6 @@ nvd0_disp_mast_init(struct nouveau_object *object) return ret; /* enable error reporting */ - nv_mask(priv, 0x610090, 0x00000001, 0x00000001); nv_mask(priv, 0x6100a0, 0x00000001, 0x00000001); /* initialise channel for dma command submission */ @@ -313,7 +336,7 @@ nvd0_disp_mast_fini(struct nouveau_object *object, bool suspend) return -EBUSY; } - /* disable error reporting */ + /* disable error reporting and completion notification */ nv_mask(priv, 0x610090, 0x00000001, 0x00000000); nv_mask(priv, 0x6100a0, 0x00000001, 0x00000000); @@ -326,6 +349,7 @@ nvd0_disp_mast_ofuncs = { .base.dtor = nv50_disp_dmac_dtor, .base.init = nvd0_disp_mast_init, .base.fini = nvd0_disp_mast_fini, + .base.ntfy = nv50_disp_chan_ntfy, .base.map = nv50_disp_chan_map, .base.rd32 = nv50_disp_chan_rd32, .base.wr32 = nv50_disp_chan_wr32, @@ -419,6 +443,7 @@ nvd0_disp_sync_ofuncs = { .base.dtor = nv50_disp_dmac_dtor, .base.init = nvd0_disp_dmac_init, .base.fini = nvd0_disp_dmac_fini, + .base.ntfy = nv50_disp_chan_ntfy, .base.map = nv50_disp_chan_map, .base.rd32 = nv50_disp_chan_rd32, .base.wr32 = nv50_disp_chan_wr32, @@ -499,6 +524,7 @@ nvd0_disp_ovly_ofuncs = { .base.dtor = nv50_disp_dmac_dtor, .base.init = nvd0_disp_dmac_init, .base.fini = nvd0_disp_dmac_fini, + .base.ntfy = nv50_disp_chan_ntfy, .base.map = nv50_disp_chan_map, .base.rd32 = nv50_disp_chan_rd32, .base.wr32 = nv50_disp_chan_wr32, @@ -524,7 +550,6 @@ nvd0_disp_pioc_init(struct nouveau_object *object) return ret; /* enable error reporting */ - nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid); nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid); /* activate channel */ @@ -553,7 +578,7 @@ nvd0_disp_pioc_fini(struct nouveau_object *object, bool suspend) return -EBUSY; } - /* disable error reporting */ + /* disable error reporting and completion notification */ nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000); nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000); @@ -570,6 +595,7 @@ nvd0_disp_oimm_ofuncs = { .base.dtor = nv50_disp_pioc_dtor, .base.init = nvd0_disp_pioc_init, .base.fini = nvd0_disp_pioc_fini, + .base.ntfy = nv50_disp_chan_ntfy, .base.map = nv50_disp_chan_map, .base.rd32 = nv50_disp_chan_rd32, .base.wr32 = nv50_disp_chan_wr32, @@ -586,6 +612,7 @@ nvd0_disp_curs_ofuncs = { .base.dtor = nv50_disp_pioc_dtor, .base.init = nvd0_disp_pioc_init, .base.fini = nvd0_disp_pioc_fini, + .base.ntfy = nv50_disp_chan_ntfy, .base.map = nv50_disp_chan_map, .base.rd32 = nv50_disp_chan_rd32, .base.wr32 = nv50_disp_chan_wr32, @@ -949,6 +976,9 @@ nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head, const int or = ffs(outp->or) - 1; const u32 ctrl = nv_rd32(priv, 0x660200 + (or * 0x020)); const u32 conf = nv_rd32(priv, 0x660404 + (head * 0x300)); + const s32 vactive = nv_rd32(priv, 0x660414 + (head * 0x300)) & 0xffff; + const s32 vblanke = nv_rd32(priv, 0x66041c + (head * 0x300)) & 0xffff; + const s32 vblanks = nv_rd32(priv, 0x660420 + (head * 0x300)) & 0xffff; const u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; const u32 link = ((ctrl & 0xf00) == 0x800) ? 0 : 1; const u32 hoff = (head * 0x800); @@ -956,23 +986,35 @@ nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head, const u32 loff = (link * 0x080) + soff; const u32 symbol = 100000; const u32 TU = 64; - u32 dpctrl = nv_rd32(priv, 0x61c10c + loff) & 0x000f0000; + u32 dpctrl = nv_rd32(priv, 0x61c10c + loff); u32 clksor = nv_rd32(priv, 0x612300 + soff); u32 datarate, link_nr, link_bw, bits; u64 ratio, value; + link_nr = hweight32(dpctrl & 0x000f0000); + link_bw = (clksor & 0x007c0000) >> 18; + link_bw *= 27000; + + /* symbols/hblank - algorithm taken from comments in tegra driver */ + value = vblanke + vactive - vblanks - 7; + value = value * link_bw; + do_div(value, pclk); + value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr); + nv_mask(priv, 0x616620 + hoff, 0x0000ffff, value); + + /* symbols/vblank - algorithm taken from comments in tegra driver */ + value = vblanks - vblanke - 25; + value = value * link_bw; + do_div(value, pclk); + value = value - ((36 / link_nr) + 3) - 1; + nv_mask(priv, 0x616624 + hoff, 0x00ffffff, value); + + /* watermark */ if ((conf & 0x3c0) == 0x180) bits = 30; else if ((conf & 0x3c0) == 0x140) bits = 24; else bits = 18; datarate = (pclk * bits) / 8; - if (dpctrl > 0x00030000) link_nr = 4; - else if (dpctrl > 0x00010000) link_nr = 2; - else link_nr = 1; - - link_bw = (clksor & 0x007c0000) >> 18; - link_bw *= 27000; - ratio = datarate; ratio *= symbol; do_div(ratio, link_nr * link_bw); @@ -1153,7 +1195,11 @@ nvd0_disp_intr(struct nouveau_subdev *subdev) if (intr & 0x00000001) { u32 stat = nv_rd32(priv, 0x61008c); - nv_wr32(priv, 0x61008c, stat); + while (stat) { + int chid = __ffs(stat); stat &= ~(1 << chid); + nv50_disp_chan_uevent_send(priv, chid); + nv_wr32(priv, 0x61008c, 1 << chid); + } intr &= ~0x00000001; } @@ -1209,6 +1255,10 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + ret = nvkm_event_init(&nvd0_disp_chan_uevent, 1, 17, &priv->uevent); + if (ret) + return ret; + nv_engine(priv)->sclass = nvd0_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nvd0_disp_intr; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c index 47fef1e398c4ca2e5c5077a689a9d957ff9a8e1b..db144b2cf06bd7a98df12c59a718a608ba5ef27b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c @@ -233,6 +233,10 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + ret = nvkm_event_init(&nvd0_disp_chan_uevent, 1, 17, &priv->uevent); + if (ret) + return ret; + nv_engine(priv)->sclass = nve0_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nvd0_disp_intr; @@ -245,7 +249,7 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->dac.sense = nv50_dac_sense; priv->sor.power = nv50_sor_power; priv->sor.hda_eld = nvd0_hda_eld; - priv->sor.hdmi = nvd0_hdmi_ctrl; + priv->sor.hdmi = nve0_hdmi_ctrl; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c index 04bda4ac4ed3dab138db7f30ca7b9b9235b10126..402d7d67d806fa0cd81dd61c758c6e868dc4cf90 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c @@ -68,6 +68,10 @@ nvf0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + ret = nvkm_event_init(&nvd0_disp_chan_uevent, 1, 17, &priv->uevent); + if (ret) + return ret; + nv_engine(priv)->sclass = nvf0_disp_base_oclass; nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nvd0_disp_intr; @@ -80,7 +84,7 @@ nvf0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->dac.sense = nv50_dac_sense; priv->sor.power = nv50_sor_power; priv->sor.hda_eld = nvd0_hda_eld; - priv->sor.hdmi = nvd0_hdmi_ctrl; + priv->sor.hdmi = nve0_hdmi_ctrl; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c index 6f6e2a898270943f4de09e88ec2307bb701d8e44..667a9070e006fdb3715e1785d3fedc1a689de8f3 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c @@ -254,7 +254,7 @@ nvkm_output_dp_create_(struct nouveau_object *parent, atomic_set(&outp->lt.done, 0); /* link maintenance */ - ret = nvkm_notify_init(&i2c->event, nvkm_output_dp_irq, true, + ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_irq, true, &(struct nvkm_i2c_ntfy_req) { .mask = NVKM_I2C_IRQ, .port = outp->base.edid->index, @@ -268,7 +268,7 @@ nvkm_output_dp_create_(struct nouveau_object *parent, } /* hotplug detect, replaces gpio-based mechanism with aux events */ - ret = nvkm_notify_init(&i2c->event, nvkm_output_dp_hpd, true, + ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_hpd, true, &(struct nvkm_i2c_ntfy_req) { .mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG, .port = outp->base.edid->index, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/priv.h b/drivers/gpu/drm/nouveau/core/engine/disp/priv.h index dbd43ae9df81af3e3978f798793334349027f631..6a0511d54ce641829796bb20a529cf16f2da214f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/priv.h +++ b/drivers/gpu/drm/nouveau/core/engine/disp/priv.h @@ -40,7 +40,8 @@ int _nouveau_disp_fini(struct nouveau_object *, bool); extern struct nouveau_oclass *nvkm_output_oclass; extern struct nouveau_oclass *nvkm_connector_oclass; -int nouveau_disp_vblank_ctor(void *data, u32 size, struct nvkm_notify *); +int nouveau_disp_vblank_ctor(struct nouveau_object *, void *data, u32 size, + struct nvkm_notify *); void nouveau_disp_vblank(struct nouveau_disp *, int head); int nouveau_disp_ntfy(struct nouveau_object *, u32, struct nvkm_event **); diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c index 0f999fc45ab9e7842ece2dc7d0f06f64568343fe..ac8375cf4eef556cba4b8b1f2b90ac541a2c51e1 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c @@ -34,7 +34,8 @@ #include static int -nouveau_fifo_event_ctor(void *data, u32 size, struct nvkm_notify *notify) +nouveau_fifo_event_ctor(struct nouveau_object *object, void *data, u32 size, + struct nvkm_notify *notify) { if (size == 0) { notify->size = 0; @@ -170,7 +171,8 @@ _nouveau_fifo_channel_wr32(struct nouveau_object *object, u64 addr, u32 data) } int -nouveau_fifo_uevent_ctor(void *data, u32 size, struct nvkm_notify *notify) +nouveau_fifo_uevent_ctor(struct nouveau_object *object, void *data, u32 size, + struct nvkm_notify *notify) { union { struct nvif_notify_uevent_req none; diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nv50.c b/drivers/gpu/drm/nouveau/core/engine/software/nv50.c index 4d2994d8cc32209800977e3008c482897b92fb4f..a0fec205f9dbe2b8c737177ccd93b5b9a75875ee 100644 --- a/drivers/gpu/drm/nouveau/core/engine/software/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/software/nv50.c @@ -175,7 +175,8 @@ nv50_software_context_ctor(struct nouveau_object *parent, return ret; for (i = 0; pdisp && i < pdisp->vblank.index_nr; i++) { - ret = nvkm_notify_init(&pdisp->vblank, pclass->vblank, false, + ret = nvkm_notify_init(NULL, &pdisp->vblank, pclass->vblank, + false, &(struct nvif_notify_head_req_v0) { .head = i, }, diff --git a/drivers/gpu/drm/nouveau/core/include/core/client.h b/drivers/gpu/drm/nouveau/core/include/core/client.h index 1794a05205d8cc5f6e9064e7e790c605ba9327f0..b0ce9f6680b5ea2f67c160a2cd64180a28324ce4 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/client.h +++ b/drivers/gpu/drm/nouveau/core/include/core/client.h @@ -48,7 +48,7 @@ int nouveau_client_init(struct nouveau_client *); int nouveau_client_fini(struct nouveau_client *, bool suspend); const char *nouveau_client_name(void *obj); -int nvkm_client_notify_new(struct nouveau_client *, struct nvkm_event *, +int nvkm_client_notify_new(struct nouveau_object *, struct nvkm_event *, void *data, u32 size); int nvkm_client_notify_del(struct nouveau_client *, int index); int nvkm_client_notify_get(struct nouveau_client *, int index); diff --git a/drivers/gpu/drm/nouveau/core/include/core/device.h b/drivers/gpu/drm/nouveau/core/include/core/device.h index 8743766454a5fd94133123aaf7acf79b6764f092..1d9d893929bb3486b1c35229ac238ea89a883dbc 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/device.h +++ b/drivers/gpu/drm/nouveau/core/include/core/device.h @@ -24,6 +24,7 @@ enum nv_subdev_type { * been created, and are allowed to assume any subdevs in the * list above them exist and have been initialised. */ + NVDEV_SUBDEV_FUSE, NVDEV_SUBDEV_MXM, NVDEV_SUBDEV_MC, NVDEV_SUBDEV_BUS, diff --git a/drivers/gpu/drm/nouveau/core/include/core/event.h b/drivers/gpu/drm/nouveau/core/include/core/event.h index 51e55d03330a406e2329edad0c42bc04be49f2e5..92876528972fd5712d9589ae843c55dabc20520f 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/event.h +++ b/drivers/gpu/drm/nouveau/core/include/core/event.h @@ -4,7 +4,8 @@ #include struct nvkm_event_func { - int (*ctor)(void *data, u32 size, struct nvkm_notify *); + int (*ctor)(struct nouveau_object *, void *data, u32 size, + struct nvkm_notify *); void (*send)(void *data, u32 size, struct nvkm_notify *); void (*init)(struct nvkm_event *, int type, int index); void (*fini)(struct nvkm_event *, int type, int index); diff --git a/drivers/gpu/drm/nouveau/core/include/core/mm.h b/drivers/gpu/drm/nouveau/core/include/core/mm.h index 2bf7d0e322611c18842b1a1c4f95c47d018c3f92..bfe6931544fe646ab4bb205bf661c9bd6adaf205 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/mm.h +++ b/drivers/gpu/drm/nouveau/core/include/core/mm.h @@ -6,6 +6,10 @@ struct nouveau_mm_node { struct list_head fl_entry; struct list_head rl_entry; +#define NVKM_MM_HEAP_ANY 0x00 + u8 heap; +#define NVKM_MM_TYPE_NONE 0x00 +#define NVKM_MM_TYPE_HOLE 0xff u8 type; u32 offset; u32 length; @@ -27,10 +31,10 @@ nouveau_mm_initialised(struct nouveau_mm *mm) int nouveau_mm_init(struct nouveau_mm *, u32 offset, u32 length, u32 block); int nouveau_mm_fini(struct nouveau_mm *); -int nouveau_mm_head(struct nouveau_mm *, u8 type, u32 size_max, u32 size_min, - u32 align, struct nouveau_mm_node **); -int nouveau_mm_tail(struct nouveau_mm *, u8 type, u32 size_max, u32 size_min, - u32 align, struct nouveau_mm_node **); +int nouveau_mm_head(struct nouveau_mm *, u8 heap, u8 type, u32 size_max, + u32 size_min, u32 align, struct nouveau_mm_node **); +int nouveau_mm_tail(struct nouveau_mm *, u8 heap, u8 type, u32 size_max, + u32 size_min, u32 align, struct nouveau_mm_node **); void nouveau_mm_free(struct nouveau_mm *, struct nouveau_mm_node **); #endif diff --git a/drivers/gpu/drm/nouveau/core/include/core/notify.h b/drivers/gpu/drm/nouveau/core/include/core/notify.h index 1262d8f020f377920e260e7c3b25c4dd4334a0e2..a7c3c5f578cc19e6a31795aaeacf817f4107d1b6 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/notify.h +++ b/drivers/gpu/drm/nouveau/core/include/core/notify.h @@ -25,8 +25,9 @@ struct nvkm_notify { const void *data; }; -int nvkm_notify_init(struct nvkm_event *, int (*func)(struct nvkm_notify *), - bool work, void *data, u32 size, u32 reply, +int nvkm_notify_init(struct nouveau_object *, struct nvkm_event *, + int (*func)(struct nvkm_notify *), bool work, + void *data, u32 size, u32 reply, struct nvkm_notify *); void nvkm_notify_fini(struct nvkm_notify *); void nvkm_notify_get(struct nvkm_notify *); diff --git a/drivers/gpu/drm/nouveau/core/include/engine/fifo.h b/drivers/gpu/drm/nouveau/core/include/engine/fifo.h index e5e4d930b2c2cee0ee1dd1c063cd17301fd8f651..2007453f6fce075e3f7deceb47f654510d861e11 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/fifo.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/fifo.h @@ -116,7 +116,8 @@ extern struct nouveau_oclass *nve0_fifo_oclass; extern struct nouveau_oclass *gk20a_fifo_oclass; extern struct nouveau_oclass *nv108_fifo_oclass; -int nouveau_fifo_uevent_ctor(void *, u32, struct nvkm_notify *); +int nouveau_fifo_uevent_ctor(struct nouveau_object *, void *, u32, + struct nvkm_notify *); void nouveau_fifo_uevent(struct nouveau_fifo *); void nv04_fifo_intr(struct nouveau_subdev *); diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bar.h b/drivers/gpu/drm/nouveau/core/include/subdev/bar.h index be037fac534c11e613564db7a5c3ace2606fb259..257ddf6d36d46fa5c7e52bb8900d6d5300ab8c45 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/bar.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bar.h @@ -12,7 +12,6 @@ struct nouveau_bar { int (*alloc)(struct nouveau_bar *, struct nouveau_object *, struct nouveau_mem *, struct nouveau_object **); - void __iomem *iomem; int (*kmap)(struct nouveau_bar *, struct nouveau_mem *, u32 flags, struct nouveau_vma *); diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/M0205.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/M0205.h new file mode 100644 index 0000000000000000000000000000000000000000..e171120cec8154c131845c4c5e31662b409e1ae8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/M0205.h @@ -0,0 +1,32 @@ +#ifndef __NVBIOS_M0205_H__ +#define __NVBIOS_M0205_H__ + +struct nvbios_M0205T { + u16 freq; +}; + +u32 nvbios_M0205Te(struct nouveau_bios *, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz); +u32 nvbios_M0205Tp(struct nouveau_bios *, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz, + struct nvbios_M0205T *); + +struct nvbios_M0205E { + u8 type; +}; + +u32 nvbios_M0205Ee(struct nouveau_bios *, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u32 nvbios_M0205Ep(struct nouveau_bios *, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_M0205E *); + +struct nvbios_M0205S { + u8 data; +}; + +u32 nvbios_M0205Se(struct nouveau_bios *, int ent, int idx, u8 *ver, u8 *hdr); +u32 nvbios_M0205Sp(struct nouveau_bios *, int ent, int idx, u8 *ver, u8 *hdr, + struct nvbios_M0205S *); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/M0209.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/M0209.h new file mode 100644 index 0000000000000000000000000000000000000000..67dc50d837bc57a6c84f8ea2f9cf9ef10fb3778f --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/M0209.h @@ -0,0 +1,30 @@ +#ifndef __NVBIOS_M0209_H__ +#define __NVBIOS_M0209_H__ + +u32 nvbios_M0209Te(struct nouveau_bios *, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz); + +struct nvbios_M0209E { + u8 v00_40; + u8 bits; + u8 modulo; + u8 v02_40; + u8 v02_07; + u8 v03; +}; + +u32 nvbios_M0209Ee(struct nouveau_bios *, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u32 nvbios_M0209Ep(struct nouveau_bios *, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_M0209E *); + +struct nvbios_M0209S { + u32 data[0x200]; +}; + +u32 nvbios_M0209Se(struct nouveau_bios *, int ent, int idx, u8 *ver, u8 *hdr); +u32 nvbios_M0209Sp(struct nouveau_bios *, int ent, int idx, u8 *ver, u8 *hdr, + struct nvbios_M0209S *); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/fan.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/fan.h new file mode 100644 index 0000000000000000000000000000000000000000..119d0874e041c6f9856779319ebea9233cdb889b --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/fan.h @@ -0,0 +1,8 @@ +#ifndef __NVBIOS_FAN_H__ +#define __NVBIOS_FAN_H__ + +#include + +u16 nvbios_fan_parse(struct nouveau_bios *bios, struct nvbios_therm_fan *fan); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/ramcfg.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/ramcfg.h index c086ac6d677d5badd33542e1c7a87047325e883e..a685bbd045680529e33d6d54e78a0359ab61c0cb 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/ramcfg.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/ramcfg.h @@ -4,60 +4,118 @@ struct nouveau_bios; struct nvbios_ramcfg { - unsigned rammap_11_08_01:1; - unsigned rammap_11_08_0c:2; - unsigned rammap_11_08_10:1; - unsigned rammap_11_11_0c:2; + unsigned rammap_ver; + unsigned rammap_hdr; + unsigned rammap_min; + unsigned rammap_max; + union { + struct { + unsigned rammap_10_04_02:1; + unsigned rammap_10_04_08:1; + }; + struct { + unsigned rammap_11_08_01:1; + unsigned rammap_11_08_0c:2; + unsigned rammap_11_08_10:1; + unsigned rammap_11_09_01ff:9; + unsigned rammap_11_0a_03fe:9; + unsigned rammap_11_0a_0400:1; + unsigned rammap_11_0a_0800:1; + unsigned rammap_11_0b_01f0:5; + unsigned rammap_11_0b_0200:1; + unsigned rammap_11_0b_0400:1; + unsigned rammap_11_0b_0800:1; + unsigned rammap_11_0d:8; + unsigned rammap_11_0e:8; + unsigned rammap_11_0f:8; + unsigned rammap_11_11_0c:2; + }; + }; - unsigned ramcfg_11_01_01:1; - unsigned ramcfg_11_01_02:1; - unsigned ramcfg_11_01_04:1; - unsigned ramcfg_11_01_08:1; - unsigned ramcfg_11_01_10:1; - unsigned ramcfg_11_01_20:1; - unsigned ramcfg_11_01_40:1; - unsigned ramcfg_11_01_80:1; - unsigned ramcfg_11_02_03:2; - unsigned ramcfg_11_02_04:1; - unsigned ramcfg_11_02_08:1; - unsigned ramcfg_11_02_10:1; - unsigned ramcfg_11_02_40:1; - unsigned ramcfg_11_02_80:1; - unsigned ramcfg_11_03_0f:4; - unsigned ramcfg_11_03_30:2; - unsigned ramcfg_11_03_c0:2; - unsigned ramcfg_11_03_f0:4; - unsigned ramcfg_11_04:8; - unsigned ramcfg_11_06:8; - unsigned ramcfg_11_07_02:1; - unsigned ramcfg_11_07_04:1; - unsigned ramcfg_11_07_08:1; - unsigned ramcfg_11_07_10:1; - unsigned ramcfg_11_07_40:1; - unsigned ramcfg_11_07_80:1; - unsigned ramcfg_11_08_01:1; - unsigned ramcfg_11_08_02:1; - unsigned ramcfg_11_08_04:1; - unsigned ramcfg_11_08_08:1; - unsigned ramcfg_11_08_10:1; - unsigned ramcfg_11_08_20:1; - unsigned ramcfg_11_09:8; + unsigned ramcfg_ver; + unsigned ramcfg_hdr; + unsigned ramcfg_timing; + union { + struct { + unsigned ramcfg_10_02_01:1; + unsigned ramcfg_10_02_02:1; + unsigned ramcfg_10_02_04:1; + unsigned ramcfg_10_02_08:1; + unsigned ramcfg_10_02_10:1; + unsigned ramcfg_10_02_20:1; + unsigned ramcfg_10_02_40:1; + unsigned ramcfg_10_03_0f:4; + unsigned ramcfg_10_05:8; + unsigned ramcfg_10_06:8; + unsigned ramcfg_10_07:8; + unsigned ramcfg_10_08:8; + unsigned ramcfg_10_09_0f:4; + unsigned ramcfg_10_09_f0:4; + }; + struct { + unsigned ramcfg_11_01_01:1; + unsigned ramcfg_11_01_02:1; + unsigned ramcfg_11_01_04:1; + unsigned ramcfg_11_01_08:1; + unsigned ramcfg_11_01_10:1; + unsigned ramcfg_11_01_20:1; + unsigned ramcfg_11_01_40:1; + unsigned ramcfg_11_01_80:1; + unsigned ramcfg_11_02_03:2; + unsigned ramcfg_11_02_04:1; + unsigned ramcfg_11_02_08:1; + unsigned ramcfg_11_02_10:1; + unsigned ramcfg_11_02_40:1; + unsigned ramcfg_11_02_80:1; + unsigned ramcfg_11_03_0f:4; + unsigned ramcfg_11_03_30:2; + unsigned ramcfg_11_03_c0:2; + unsigned ramcfg_11_03_f0:4; + unsigned ramcfg_11_04:8; + unsigned ramcfg_11_06:8; + unsigned ramcfg_11_07_02:1; + unsigned ramcfg_11_07_04:1; + unsigned ramcfg_11_07_08:1; + unsigned ramcfg_11_07_10:1; + unsigned ramcfg_11_07_40:1; + unsigned ramcfg_11_07_80:1; + unsigned ramcfg_11_08_01:1; + unsigned ramcfg_11_08_02:1; + unsigned ramcfg_11_08_04:1; + unsigned ramcfg_11_08_08:1; + unsigned ramcfg_11_08_10:1; + unsigned ramcfg_11_08_20:1; + unsigned ramcfg_11_09:8; + }; + }; + unsigned timing_ver; + unsigned timing_hdr; unsigned timing[11]; - unsigned timing_20_2e_03:2; - unsigned timing_20_2e_30:2; - unsigned timing_20_2e_c0:2; - unsigned timing_20_2f_03:2; - unsigned timing_20_2c_003f:6; - unsigned timing_20_2c_1fc0:7; - unsigned timing_20_30_f8:5; - unsigned timing_20_30_07:3; - unsigned timing_20_31_0007:3; - unsigned timing_20_31_0078:4; - unsigned timing_20_31_0780:4; - unsigned timing_20_31_0800:1; - unsigned timing_20_31_7000:3; - unsigned timing_20_31_8000:1; + union { + struct { + unsigned timing_10_WR:8; + unsigned timing_10_CL:8; + unsigned timing_10_ODT:3; + unsigned timing_10_CWL:8; + }; + struct { + unsigned timing_20_2e_03:2; + unsigned timing_20_2e_30:2; + unsigned timing_20_2e_c0:2; + unsigned timing_20_2f_03:2; + unsigned timing_20_2c_003f:6; + unsigned timing_20_2c_1fc0:7; + unsigned timing_20_30_f8:5; + unsigned timing_20_30_07:3; + unsigned timing_20_31_0007:3; + unsigned timing_20_31_0078:4; + unsigned timing_20_31_0780:4; + unsigned timing_20_31_0800:1; + unsigned timing_20_31_7000:3; + unsigned timing_20_31_8000:1; + }; + }; }; u8 nvbios_ramcfg_count(struct nouveau_bios *); diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/rammap.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/rammap.h index 5bdf8e4db40a137df8b00f4a0183424929ad828c..47e021d3e20dd34ee76912bb2b717d4640bbe32b 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/rammap.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/rammap.h @@ -8,9 +8,10 @@ u32 nvbios_rammapTe(struct nouveau_bios *, u8 *ver, u8 *hdr, u32 nvbios_rammapEe(struct nouveau_bios *, int idx, u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u32 nvbios_rammapEp(struct nouveau_bios *, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_ramcfg *); u32 nvbios_rammapEm(struct nouveau_bios *, u16 mhz, - u8 *ver, u8 *hdr, u8 *cnt, u8 *len); -u32 nvbios_rammapEp(struct nouveau_bios *, u16 mhz, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *); diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h index 8dc5051df55dfdddadc6a33c0e315369b6017a3d..295d093f3b3003c2505ac2e92dd350781beaa1a9 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h @@ -23,6 +23,12 @@ struct nvbios_therm_sensor { struct nvbios_therm_threshold thrs_shutdown; }; +enum nvbios_therm_fan_type { + NVBIOS_THERM_FAN_UNK = 0, + NVBIOS_THERM_FAN_TOGGLE = 1, + NVBIOS_THERM_FAN_PWM = 2, +}; + /* no vbios have more than 6 */ #define NOUVEAU_TEMP_FAN_TRIP_MAX 10 struct nouveau_therm_trip_point { @@ -38,7 +44,9 @@ enum nvbios_therm_fan_mode { }; struct nvbios_therm_fan { - u16 pwm_freq; + enum nvbios_therm_fan_type type; + + u32 pwm_freq; u8 min_duty; u8 max_duty; diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/clock.h b/drivers/gpu/drm/nouveau/core/include/subdev/clock.h index a5ca00dd2f6166fa77a5fb3571fb37eb055d86fd..36ed035d4d421db3ad5b6dc10b563cf46e0280e6 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/clock.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/clock.h @@ -29,6 +29,7 @@ enum nv_clk_src { nv_clk_src_mdiv, nv_clk_src_core, + nv_clk_src_core_intm, nv_clk_src_shader, nv_clk_src_mem, diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/fb.h b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h index 871e73914b24eb7b75ca6d2d952a16ed4cc72ecc..8d0032f152054f57dbe0b0e4f35e784b50171e56 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/fb.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h @@ -111,6 +111,7 @@ extern struct nouveau_oclass *gm107_fb_oclass; #include struct nouveau_ram_data { + struct list_head head; struct nvbios_ramcfg bios; u32 freq; }; @@ -136,6 +137,7 @@ struct nouveau_ram { int ranks; int parts; + int part_mask; int (*get)(struct nouveau_fb *, u64 size, u32 align, u32 size_nc, u32 type, struct nouveau_mem **); @@ -144,11 +146,6 @@ struct nouveau_ram { int (*calc)(struct nouveau_fb *, u32 freq); int (*prog)(struct nouveau_fb *); void (*tidy)(struct nouveau_fb *); - struct { - u8 version; - u32 data; - u8 size; - } rammap, ramcfg, timing; u32 freq; u32 mr[16]; u32 mr1_nuts; diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/fb/regsnv04.h b/drivers/gpu/drm/nouveau/core/include/subdev/fb/regsnv04.h new file mode 100644 index 0000000000000000000000000000000000000000..0f7fc0c52ab2cc88b8372dce19537b9e651d04aa --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/subdev/fb/regsnv04.h @@ -0,0 +1,21 @@ +#ifndef __NOUVEAU_FB_REGS_04_H__ +#define __NOUVEAU_FB_REGS_04_H__ + +#define NV04_PFB_BOOT_0 0x00100000 +# define NV04_PFB_BOOT_0_RAM_AMOUNT 0x00000003 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB 0x00000000 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB 0x00000001 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB 0x00000002 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB 0x00000003 +# define NV04_PFB_BOOT_0_RAM_WIDTH_128 0x00000004 +# define NV04_PFB_BOOT_0_RAM_TYPE 0x00000028 +# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x00000000 +# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x00000008 +# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x00000010 +# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x00000018 +# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x00000020 +# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x00000028 +# define NV04_PFB_BOOT_0_UMA_ENABLE 0x00000100 +# define NV04_PFB_BOOT_0_UMA_SIZE 0x0000f000 + +#endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/fuse.h b/drivers/gpu/drm/nouveau/core/include/subdev/fuse.h new file mode 100644 index 0000000000000000000000000000000000000000..2b1ddb2a9a7df17d6f0ce1a6257c91b5236cdda6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/subdev/fuse.h @@ -0,0 +1,30 @@ +#ifndef __NOUVEAU_FUSE_H__ +#define __NOUVEAU_FUSE_H__ + +#include +#include + +struct nouveau_fuse { + struct nouveau_subdev base; +}; + +static inline struct nouveau_fuse * +nouveau_fuse(void *obj) +{ + return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_FUSE]; +} + +#define nouveau_fuse_create(p, e, o, d) \ + nouveau_fuse_create_((p), (e), (o), sizeof(**d), (void **)d) + +int nouveau_fuse_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, int, void **); +void _nouveau_fuse_dtor(struct nouveau_object *); +int _nouveau_fuse_init(struct nouveau_object *); +#define _nouveau_fuse_fini _nouveau_subdev_fini + +extern struct nouveau_oclass g80_fuse_oclass; +extern struct nouveau_oclass gf100_fuse_oclass; +extern struct nouveau_oclass gm107_fuse_oclass; + +#endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h b/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h index b73733d21cc71c64d9871a0b8685cf21e3a2fc75..f855140dbcb70d3c081bbadaf941cfe25a5a14c2 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h @@ -40,7 +40,7 @@ nouveau_gpio(void *obj) extern struct nouveau_oclass *nv10_gpio_oclass; extern struct nouveau_oclass *nv50_gpio_oclass; -extern struct nouveau_oclass *nv92_gpio_oclass; +extern struct nouveau_oclass *nv94_gpio_oclass; extern struct nouveau_oclass *nvd0_gpio_oclass; extern struct nouveau_oclass *nve0_gpio_oclass; diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/pwr.h b/drivers/gpu/drm/nouveau/core/include/subdev/pwr.h index f73feec151db453eb8160fba6263dd7999e808fe..bf3d1f6113331a72045c7f154eadb04c64ebc9f6 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/pwr.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/pwr.h @@ -47,5 +47,8 @@ void nouveau_memx_wr32(struct nouveau_memx *, u32 addr, u32 data); void nouveau_memx_wait(struct nouveau_memx *, u32 addr, u32 mask, u32 data, u32 nsec); void nouveau_memx_nsec(struct nouveau_memx *, u32 nsec); +void nouveau_memx_wait_vblank(struct nouveau_memx *); +void nouveau_memx_block(struct nouveau_memx *); +void nouveau_memx_unblock(struct nouveau_memx *); #endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/therm.h b/drivers/gpu/drm/nouveau/core/include/subdev/therm.h index d4a68179e5863c383640cd9c339ce941e34d9909..a437597dcafc5f58e0fe08953e85037d2b073f97 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/therm.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/therm.h @@ -78,5 +78,6 @@ extern struct nouveau_oclass nv50_therm_oclass; extern struct nouveau_oclass nv84_therm_oclass; extern struct nouveau_oclass nva3_therm_oclass; extern struct nouveau_oclass nvd0_therm_oclass; +extern struct nouveau_oclass gm107_therm_oclass; #endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/bar/base.c b/drivers/gpu/drm/nouveau/core/subdev/bar/base.c index 8bcbdf39cfb2db0844a5c030494096488cfc40e2..b1adc69efd88154670b09852789f2ac3413501ab 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bar/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bar/base.c @@ -38,10 +38,12 @@ struct nouveau_barobj { static int nouveau_barobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *mem, u32 size, + struct nouveau_oclass *oclass, void *data, u32 size, struct nouveau_object **pobject) { + struct nouveau_device *device = nv_device(parent); struct nouveau_bar *bar = (void *)engine; + struct nouveau_mem *mem = data; struct nouveau_barobj *barobj; int ret; @@ -54,7 +56,13 @@ nouveau_barobj_ctor(struct nouveau_object *parent, if (ret) return ret; - barobj->iomem = bar->iomem + (u32)barobj->vma.offset; + barobj->iomem = ioremap(nv_device_resource_start(device, 3) + + (u32)barobj->vma.offset, mem->size << 12); + if (!barobj->iomem) { + nv_warn(bar, "PRAMIN ioremap failed\n"); + return -ENOMEM; + } + return 0; } @@ -63,8 +71,11 @@ nouveau_barobj_dtor(struct nouveau_object *object) { struct nouveau_bar *bar = (void *)object->engine; struct nouveau_barobj *barobj = (void *)object; - if (barobj->vma.node) + if (barobj->vma.node) { + if (barobj->iomem) + iounmap(barobj->iomem); bar->unmap(bar, &barobj->vma); + } nouveau_object_destroy(&barobj->base); } @@ -99,12 +110,11 @@ nouveau_bar_alloc(struct nouveau_bar *bar, struct nouveau_object *parent, struct nouveau_mem *mem, struct nouveau_object **pobject) { struct nouveau_object *engine = nv_object(bar); - int ret = -ENOMEM; - if (bar->iomem) { - ret = nouveau_object_ctor(parent, engine, - &nouveau_barobj_oclass, - mem, 0, pobject); - } + struct nouveau_object *gpuobj; + int ret = nouveau_object_ctor(parent, engine, &nouveau_barobj_oclass, + mem, 0, &gpuobj); + if (ret == 0) + *pobject = gpuobj; return ret; } @@ -113,7 +123,6 @@ nouveau_bar_create_(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, int length, void **pobject) { - struct nouveau_device *device = nv_device(parent); struct nouveau_bar *bar; int ret; @@ -123,21 +132,12 @@ nouveau_bar_create_(struct nouveau_object *parent, if (ret) return ret; - if (nv_device_resource_len(device, 3) != 0) { - bar->iomem = ioremap(nv_device_resource_start(device, 3), - nv_device_resource_len(device, 3)); - if (!bar->iomem) - nv_warn(bar, "PRAMIN ioremap failed\n"); - } - return 0; } void nouveau_bar_destroy(struct nouveau_bar *bar) { - if (bar->iomem) - iounmap(bar->iomem); nouveau_subdev_destroy(&bar->base); } diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/M0205.c b/drivers/gpu/drm/nouveau/core/subdev/bios/M0205.c new file mode 100644 index 0000000000000000000000000000000000000000..ac9617c5fc2a4242629065d085641e25c06adeaa --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/M0205.c @@ -0,0 +1,136 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * 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. + * + * Authors: Ben Skeggs + */ + +#include +#include +#include + +u32 +nvbios_M0205Te(struct nouveau_bios *bios, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz) +{ + struct bit_entry bit_M; + u32 data = 0x00000000; + + if (!bit_entry(bios, 'M', &bit_M)) { + if (bit_M.version == 2 && bit_M.length > 0x08) + data = nv_ro32(bios, bit_M.offset + 0x05); + if (data) { + *ver = nv_ro08(bios, data + 0x00); + switch (*ver) { + case 0x10: + *hdr = nv_ro08(bios, data + 0x01); + *len = nv_ro08(bios, data + 0x02); + *ssz = nv_ro08(bios, data + 0x03); + *snr = nv_ro08(bios, data + 0x04); + *cnt = nv_ro08(bios, data + 0x05); + return data; + default: + break; + } + } + } + + return 0x00000000; +} + +u32 +nvbios_M0205Tp(struct nouveau_bios *bios, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz, + struct nvbios_M0205T *info) +{ + u32 data = nvbios_M0205Te(bios, ver, hdr, cnt, len, snr, ssz); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x10: + info->freq = nv_ro16(bios, data + 0x06); + break; + default: + break; + } + return data; +} + +u32 +nvbios_M0205Ee(struct nouveau_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u8 snr, ssz; + u32 data = nvbios_M0205Te(bios, ver, hdr, cnt, len, &snr, &ssz); + if (data && idx < *cnt) { + data = data + *hdr + idx * (*len + (snr * ssz)); + *hdr = *len; + *cnt = snr; + *len = ssz; + return data; + } + return 0x00000000; +} + +u32 +nvbios_M0205Ep(struct nouveau_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_M0205E *info) +{ + u32 data = nvbios_M0205Ee(bios, idx, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x10: + info->type = nv_ro08(bios, data + 0x00) & 0x0f; + return data; + default: + break; + } + return 0x00000000; +} + +u32 +nvbios_M0205Se(struct nouveau_bios *bios, int ent, int idx, u8 *ver, u8 *hdr) +{ + + u8 cnt, len; + u32 data = nvbios_M0205Ee(bios, ent, ver, hdr, &cnt, &len); + if (data && idx < cnt) { + data = data + *hdr + idx * len; + *hdr = len; + return data; + } + return 0x00000000; +} + +u32 +nvbios_M0205Sp(struct nouveau_bios *bios, int ent, int idx, u8 *ver, u8 *hdr, + struct nvbios_M0205S *info) +{ + u32 data = nvbios_M0205Se(bios, ent, idx, ver, hdr); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x10: + info->data = nv_ro08(bios, data + 0x00); + return data; + default: + break; + } + return 0x00000000; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/M0209.c b/drivers/gpu/drm/nouveau/core/subdev/bios/M0209.c new file mode 100644 index 0000000000000000000000000000000000000000..b142a510e89fb3abb03591a43e1fcad549058fd7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/M0209.c @@ -0,0 +1,137 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * 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. + * + * Authors: Ben Skeggs + */ + +#include +#include +#include + +u32 +nvbios_M0209Te(struct nouveau_bios *bios, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz) +{ + struct bit_entry bit_M; + u32 data = 0x00000000; + + if (!bit_entry(bios, 'M', &bit_M)) { + if (bit_M.version == 2 && bit_M.length > 0x0c) + data = nv_ro32(bios, bit_M.offset + 0x09); + if (data) { + *ver = nv_ro08(bios, data + 0x00); + switch (*ver) { + case 0x10: + *hdr = nv_ro08(bios, data + 0x01); + *len = nv_ro08(bios, data + 0x02); + *ssz = nv_ro08(bios, data + 0x03); + *snr = 1; + *cnt = nv_ro08(bios, data + 0x04); + return data; + default: + break; + } + } + } + + return 0x00000000; +} + +u32 +nvbios_M0209Ee(struct nouveau_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u8 snr, ssz; + u32 data = nvbios_M0209Te(bios, ver, hdr, cnt, len, &snr, &ssz); + if (data && idx < *cnt) { + data = data + *hdr + idx * (*len + (snr * ssz)); + *hdr = *len; + *cnt = snr; + *len = ssz; + return data; + } + return 0x00000000; +} + +u32 +nvbios_M0209Ep(struct nouveau_bios *bios, int idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_M0209E *info) +{ + u32 data = nvbios_M0209Ee(bios, idx, ver, hdr, cnt, len); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x10: + info->v00_40 = (nv_ro08(bios, data + 0x00) & 0x40) >> 6; + info->bits = nv_ro08(bios, data + 0x00) & 0x3f; + info->modulo = nv_ro08(bios, data + 0x01); + info->v02_40 = (nv_ro08(bios, data + 0x02) & 0x40) >> 6; + info->v02_07 = nv_ro08(bios, data + 0x02) & 0x07; + info->v03 = nv_ro08(bios, data + 0x03); + return data; + default: + break; + } + return 0x00000000; +} + +u32 +nvbios_M0209Se(struct nouveau_bios *bios, int ent, int idx, u8 *ver, u8 *hdr) +{ + + u8 cnt, len; + u32 data = nvbios_M0209Ee(bios, ent, ver, hdr, &cnt, &len); + if (data && idx < cnt) { + data = data + *hdr + idx * len; + *hdr = len; + return data; + } + return 0x00000000; +} + +u32 +nvbios_M0209Sp(struct nouveau_bios *bios, int ent, int idx, u8 *ver, u8 *hdr, + struct nvbios_M0209S *info) +{ + struct nvbios_M0209E M0209E; + u8 cnt, len; + u32 data = nvbios_M0209Ep(bios, ent, ver, hdr, &cnt, &len, &M0209E); + if (data) { + u32 i, data = nvbios_M0209Se(bios, ent, idx, ver, hdr); + memset(info, 0x00, sizeof(*info)); + switch (!!data * *ver) { + case 0x10: + for (i = 0; i < ARRAY_SIZE(info->data); i++) { + u32 bits = (i % M0209E.modulo) * M0209E.bits; + u32 mask = (1ULL << M0209E.bits) - 1; + u16 off = bits / 8; + u8 mod = bits % 8; + info->data[i] = nv_ro32(bios, data + off); + info->data[i] = info->data[i] >> mod; + info->data[i] = info->data[i] & mask; + } + return data; + default: + break; + } + } + return 0x00000000; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c b/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c index 88606bfaf84742c3c87a26d0d8387c29fecc9d81..bd8d348385b38fe1a3231586bdea941be9657558 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c @@ -124,6 +124,7 @@ dcb_outp_parse(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len, struct dcb_output *outp) { u16 dcb = dcb_outp(bios, idx, ver, len); + memset(outp, 0x00, sizeof(*outp)); if (dcb) { if (*ver >= 0x20) { u32 conn = nv_ro32(bios, dcb + 0x00); diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/fan.c b/drivers/gpu/drm/nouveau/core/subdev/bios/fan.c new file mode 100644 index 0000000000000000000000000000000000000000..e419892240f5eee0665516b813bf6c3cbdca1650 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/fan.c @@ -0,0 +1,93 @@ +/* + * Copyright 2014 Martin Peres + * + * 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. + * + * Authors: Martin Peres + */ + +#include +#include +#include + +u16 +nvbios_fan_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + struct bit_entry bit_P; + u16 fan = 0x0000; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 2 && bit_P.length >= 0x5a) + fan = nv_ro16(bios, bit_P.offset + 0x58); + + if (fan) { + *ver = nv_ro08(bios, fan + 0); + switch (*ver) { + case 0x10: + *hdr = nv_ro08(bios, fan + 1); + *len = nv_ro08(bios, fan + 2); + *cnt = nv_ro08(bios, fan + 3); + return fan; + default: + break; + } + } + } + + return 0x0000; +} + +u16 +nvbios_fan_entry(struct nouveau_bios *bios, int idx, u8 *ver, u8 *hdr, + u8 *cnt, u8 *len) +{ + u16 data = nvbios_fan_table(bios, ver, hdr, cnt, len); + if (data && idx < *cnt) + return data + *hdr + (idx * (*len)); + return 0x0000; +} + +u16 +nvbios_fan_parse(struct nouveau_bios *bios, struct nvbios_therm_fan *fan) +{ + u8 ver, hdr, cnt, len; + + u16 data = nvbios_fan_entry(bios, 0, &ver, &hdr, &cnt, &len); + if (data) { + u8 type = nv_ro08(bios, data + 0x00); + switch (type) { + case 0: + fan->type = NVBIOS_THERM_FAN_TOGGLE; + break; + case 1: + case 2: + /* TODO: Understand the difference between the two! */ + fan->type = NVBIOS_THERM_FAN_PWM; + break; + default: + fan->type = NVBIOS_THERM_FAN_UNK; + } + + fan->min_duty = nv_ro08(bios, data + 0x02); + fan->max_duty = nv_ro08(bios, data + 0x03); + + fan->pwm_freq = nv_ro32(bios, data + 0x0b) & 0xffffff; + } + return data; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/rammap.c b/drivers/gpu/drm/nouveau/core/subdev/bios/rammap.c index 1811b2cb047276ead557d6d1025568667a1fbc24..585e69331ccce91ac3bc694e7fc09942a06ba993 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/rammap.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/rammap.c @@ -75,31 +75,39 @@ nvbios_rammapEe(struct nouveau_bios *bios, int idx, } u32 -nvbios_rammapEm(struct nouveau_bios *bios, u16 khz, - u8 *ver, u8 *hdr, u8 *cnt, u8 *len) -{ - int idx = 0; - u32 data; - while ((data = nvbios_rammapEe(bios, idx++, ver, hdr, cnt, len))) { - if (khz >= nv_ro16(bios, data + 0x00) && - khz <= nv_ro16(bios, data + 0x02)) - break; - } - return data; -} - -u32 -nvbios_rammapEp(struct nouveau_bios *bios, u16 khz, +nvbios_rammapEp(struct nouveau_bios *bios, int idx, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ramcfg *p) { - u32 data = nvbios_rammapEm(bios, khz, ver, hdr, cnt, len); + u32 data = nvbios_rammapEe(bios, idx, ver, hdr, cnt, len), temp; memset(p, 0x00, sizeof(*p)); + p->rammap_ver = *ver; + p->rammap_hdr = *hdr; switch (!!data * *ver) { + case 0x10: + p->rammap_min = nv_ro16(bios, data + 0x00); + p->rammap_max = nv_ro16(bios, data + 0x02); + p->rammap_10_04_02 = (nv_ro08(bios, data + 0x04) & 0x02) >> 1; + p->rammap_10_04_08 = (nv_ro08(bios, data + 0x04) & 0x08) >> 3; + break; case 0x11: + p->rammap_min = nv_ro16(bios, data + 0x00); + p->rammap_max = nv_ro16(bios, data + 0x02); p->rammap_11_08_01 = (nv_ro08(bios, data + 0x08) & 0x01) >> 0; p->rammap_11_08_0c = (nv_ro08(bios, data + 0x08) & 0x0c) >> 2; p->rammap_11_08_10 = (nv_ro08(bios, data + 0x08) & 0x10) >> 4; + temp = nv_ro32(bios, data + 0x09); + p->rammap_11_09_01ff = (temp & 0x000001ff) >> 0; + p->rammap_11_0a_03fe = (temp & 0x0003fe00) >> 9; + p->rammap_11_0a_0400 = (temp & 0x00040000) >> 18; + p->rammap_11_0a_0800 = (temp & 0x00080000) >> 19; + p->rammap_11_0b_01f0 = (temp & 0x01f00000) >> 20; + p->rammap_11_0b_0200 = (temp & 0x02000000) >> 25; + p->rammap_11_0b_0400 = (temp & 0x04000000) >> 26; + p->rammap_11_0b_0800 = (temp & 0x08000000) >> 27; + p->rammap_11_0d = nv_ro08(bios, data + 0x0d); + p->rammap_11_0e = nv_ro08(bios, data + 0x0e); + p->rammap_11_0f = nv_ro08(bios, data + 0x0f); p->rammap_11_11_0c = (nv_ro08(bios, data + 0x11) & 0x0c) >> 2; break; default: @@ -109,6 +117,20 @@ nvbios_rammapEp(struct nouveau_bios *bios, u16 khz, return data; } +u32 +nvbios_rammapEm(struct nouveau_bios *bios, u16 mhz, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_ramcfg *info) +{ + int idx = 0; + u32 data; + while ((data = nvbios_rammapEp(bios, idx++, ver, hdr, cnt, len, info))) { + if (mhz >= info->rammap_min && mhz <= info->rammap_max) + break; + } + return data; +} + u32 nvbios_rammapSe(struct nouveau_bios *bios, u32 data, u8 ever, u8 ehdr, u8 ecnt, u8 elen, int idx, @@ -129,8 +151,28 @@ nvbios_rammapSp(struct nouveau_bios *bios, u32 data, u8 *ver, u8 *hdr, struct nvbios_ramcfg *p) { data = nvbios_rammapSe(bios, data, ever, ehdr, ecnt, elen, idx, ver, hdr); + p->ramcfg_ver = *ver; + p->ramcfg_hdr = *hdr; switch (!!data * *ver) { + case 0x10: + p->ramcfg_timing = nv_ro08(bios, data + 0x01); + p->ramcfg_10_02_01 = (nv_ro08(bios, data + 0x02) & 0x01) >> 0; + p->ramcfg_10_02_02 = (nv_ro08(bios, data + 0x02) & 0x02) >> 1; + p->ramcfg_10_02_04 = (nv_ro08(bios, data + 0x02) & 0x04) >> 2; + p->ramcfg_10_02_08 = (nv_ro08(bios, data + 0x02) & 0x08) >> 3; + p->ramcfg_10_02_10 = (nv_ro08(bios, data + 0x02) & 0x10) >> 4; + p->ramcfg_10_02_20 = (nv_ro08(bios, data + 0x02) & 0x20) >> 5; + p->ramcfg_10_02_40 = (nv_ro08(bios, data + 0x02) & 0x40) >> 6; + p->ramcfg_10_03_0f = (nv_ro08(bios, data + 0x03) & 0x0f) >> 0; + p->ramcfg_10_05 = (nv_ro08(bios, data + 0x05) & 0xff) >> 0; + p->ramcfg_10_06 = (nv_ro08(bios, data + 0x06) & 0xff) >> 0; + p->ramcfg_10_07 = (nv_ro08(bios, data + 0x07) & 0xff) >> 0; + p->ramcfg_10_08 = (nv_ro08(bios, data + 0x08) & 0xff) >> 0; + p->ramcfg_10_09_0f = (nv_ro08(bios, data + 0x09) & 0x0f) >> 0; + p->ramcfg_10_09_f0 = (nv_ro08(bios, data + 0x09) & 0xf0) >> 4; + break; case 0x11: + p->ramcfg_timing = nv_ro08(bios, data + 0x00); p->ramcfg_11_01_01 = (nv_ro08(bios, data + 0x01) & 0x01) >> 0; p->ramcfg_11_01_02 = (nv_ro08(bios, data + 0x01) & 0x02) >> 1; p->ramcfg_11_01_04 = (nv_ro08(bios, data + 0x01) & 0x04) >> 2; diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/timing.c b/drivers/gpu/drm/nouveau/core/subdev/bios/timing.c index 350d44ab2ba24b9e56a9dbf178a86497159cc326..46d955eb51eba6f956b238d0134fb2043fec7bd4 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/timing.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/timing.c @@ -89,7 +89,15 @@ nvbios_timingEp(struct nouveau_bios *bios, int idx, struct nvbios_ramcfg *p) { u16 data = nvbios_timingEe(bios, idx, ver, hdr, cnt, len), temp; + p->timing_ver = *ver; + p->timing_hdr = *hdr; switch (!!data * *ver) { + case 0x10: + p->timing_10_WR = nv_ro08(bios, data + 0x00); + p->timing_10_CL = nv_ro08(bios, data + 0x02); + p->timing_10_ODT = nv_ro08(bios, data + 0x0e) & 0x07; + p->timing_10_CWL = nv_ro08(bios, data + 0x13); + break; case 0x20: p->timing[0] = nv_ro32(bios, data + 0x00); p->timing[1] = nv_ro32(bios, data + 0x04); diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/base.c b/drivers/gpu/drm/nouveau/core/subdev/clock/base.c index a276a711294a21e3bc6be1bbe102444df26e2ad6..e51b72d471293e304053c46eb4890c892f6a94a0 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/base.c @@ -573,7 +573,7 @@ nouveau_clock_create_(struct nouveau_object *parent, clk->allow_reclock = allow_reclock; - ret = nvkm_notify_init(&device->event, nouveau_clock_pwrsrc, true, + ret = nvkm_notify_init(NULL, &device->event, nouveau_clock_pwrsrc, true, NULL, 0, 0, &clk->pwrsrc_ntfy); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c index 087012b189569ee8cf21e1d2f02327023093f25d..094551d8ad9b44619f9950c30c6553d1f0a5b730 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c @@ -20,8 +20,10 @@ * OTHER DEALINGS IN THE SOFTWARE. * * Authors: Ben Skeggs + * Roy Spliet */ +#include #include #include #include @@ -42,9 +44,17 @@ static u32 read_vco(struct nva3_clock_priv *priv, int clk) { u32 sctl = nv_rd32(priv, 0x4120 + (clk * 4)); - if ((sctl & 0x00000030) != 0x00000030) + + switch (sctl & 0x00000030) { + case 0x00000000: + return nv_device(priv)->crystal; + case 0x00000020: return read_pll(priv, 0x41, 0x00e820); - return read_pll(priv, 0x42, 0x00e8a0); + case 0x00000030: + return read_pll(priv, 0x42, 0x00e8a0); + default: + return 0; + } } static u32 @@ -66,14 +76,25 @@ read_clk(struct nva3_clock_priv *priv, int clk, bool ignore_en) if (!ignore_en && !(sctl & 0x00000100)) return 0; + /* out_alt */ + if (sctl & 0x00000400) + return 108000; + + /* vco_out */ switch (sctl & 0x00003000) { case 0x00000000: - return nv_device(priv)->crystal; + if (!(sctl & 0x00000200)) + return nv_device(priv)->crystal; + return 0; case 0x00002000: if (sctl & 0x00000040) return 108000; return 100000; case 0x00003000: + /* vco_enable */ + if (!(sctl & 0x00000001)) + return 0; + sclk = read_vco(priv, clk); sdiv = ((sctl & 0x003f0000) >> 16) + 2; return (sclk * 2) / sdiv; @@ -95,7 +116,9 @@ read_pll(struct nva3_clock_priv *priv, int clk, u32 pll) N = (coef & 0x0000ff00) >> 8; P = (coef & 0x003f0000) >> 16; - /* no post-divider on these.. */ + /* no post-divider on these.. + * XXX: it looks more like two post-"dividers" that + * cross each other out in the default RPLL config */ if ((pll & 0x00ff00) == 0x00e800) P = 1; @@ -114,13 +137,13 @@ static int nva3_clock_read(struct nouveau_clock *clk, enum nv_clk_src src) { struct nva3_clock_priv *priv = (void *)clk; + u32 hsrc; switch (src) { case nv_clk_src_crystal: return nv_device(priv)->crystal; - case nv_clk_src_href: - return 100000; case nv_clk_src_core: + case nv_clk_src_core_intm: return read_pll(priv, 0x00, 0x4200); case nv_clk_src_shader: return read_pll(priv, 0x01, 0x4220); @@ -132,24 +155,33 @@ nva3_clock_read(struct nouveau_clock *clk, enum nv_clk_src src) return read_clk(priv, 0x21, false); case nv_clk_src_daemon: return read_clk(priv, 0x25, false); + case nv_clk_src_host: + hsrc = (nv_rd32(priv, 0xc040) & 0x30000000) >> 28; + switch (hsrc) { + case 0: + return read_clk(priv, 0x1d, false); + case 2: + case 3: + return 277000; + default: + nv_error(clk, "unknown HOST clock source %d\n", hsrc); + return -EINVAL; + } default: nv_error(clk, "invalid clock source %d\n", src); return -EINVAL; } + + return 0; } int -nva3_clock_info(struct nouveau_clock *clock, int clk, u32 pll, u32 khz, +nva3_clk_info(struct nouveau_clock *clock, int clk, u32 khz, struct nva3_clock_info *info) { - struct nouveau_bios *bios = nouveau_bios(clock); struct nva3_clock_priv *priv = (void *)clock; - struct nvbios_pll limits; - u32 oclk, sclk, sdiv; - int P, N, M, diff; - int ret; + u32 oclk, sclk, sdiv, diff; - info->pll = 0; info->clk = 0; switch (khz) { @@ -164,43 +196,69 @@ nva3_clock_info(struct nouveau_clock *clock, int clk, u32 pll, u32 khz, return khz; default: sclk = read_vco(priv, clk); - sdiv = min((sclk * 2) / (khz - 2999), (u32)65); - /* if the clock has a PLL attached, and we can get a within - * [-2, 3) MHz of a divider, we'll disable the PLL and use - * the divider instead. - * - * divider can go as low as 2, limited here because NVIDIA + sdiv = min((sclk * 2) / khz, (u32)65); + oclk = (sclk * 2) / sdiv; + diff = ((khz + 3000) - oclk); + + /* When imprecise, play it safe and aim for a clock lower than + * desired rather than higher */ + if (diff < 0) { + sdiv++; + oclk = (sclk * 2) / sdiv; + } + + /* divider can go as low as 2, limited here because NVIDIA * and the VBIOS on my NVA8 seem to prefer using the PLL * for 810MHz - is there a good reason? - */ + * XXX: PLLs with refclk 810MHz? */ if (sdiv > 4) { - oclk = (sclk * 2) / sdiv; - diff = khz - oclk; - if (!pll || (diff >= -2000 && diff < 3000)) { - info->clk = (((sdiv - 2) << 16) | 0x00003100); - return oclk; - } + info->clk = (((sdiv - 2) << 16) | 0x00003100); + return oclk; } - if (!pll) - return -ERANGE; break; } + return -ERANGE; +} + +int +nva3_pll_info(struct nouveau_clock *clock, int clk, u32 pll, u32 khz, + struct nva3_clock_info *info) +{ + struct nouveau_bios *bios = nouveau_bios(clock); + struct nva3_clock_priv *priv = (void *)clock; + struct nvbios_pll limits; + int P, N, M, diff; + int ret; + + info->pll = 0; + + /* If we can get a within [-2, 3) MHz of a divider, we'll disable the + * PLL and use the divider instead. */ + ret = nva3_clk_info(clock, clk, khz, info); + diff = khz - ret; + if (!pll || (diff >= -2000 && diff < 3000)) { + goto out; + } + + /* Try with PLL */ ret = nvbios_pll_parse(bios, pll, &limits); if (ret) return ret; - limits.refclk = read_clk(priv, clk - 0x10, true); - if (!limits.refclk) + ret = nva3_clk_info(clock, clk - 0x10, limits.refclk, info); + if (ret != limits.refclk) return -EINVAL; ret = nva3_pll_calc(nv_subdev(priv), &limits, khz, &N, NULL, &M, &P); if (ret >= 0) { - info->clk = nv_rd32(priv, 0x4120 + (clk * 4)); info->pll = (P << 16) | (N << 8) | M; } +out: + info->fb_delay = max(((khz + 7566) / 15133), (u32) 18); + return ret ? ret : -ERANGE; } @@ -208,13 +266,76 @@ static int calc_clk(struct nva3_clock_priv *priv, struct nouveau_cstate *cstate, int clk, u32 pll, int idx) { - int ret = nva3_clock_info(&priv->base, clk, pll, cstate->domain[idx], + int ret = nva3_pll_info(&priv->base, clk, pll, cstate->domain[idx], &priv->eng[idx]); if (ret >= 0) return 0; return ret; } +static int +calc_host(struct nva3_clock_priv *priv, struct nouveau_cstate *cstate) +{ + int ret = 0; + u32 kHz = cstate->domain[nv_clk_src_host]; + struct nva3_clock_info *info = &priv->eng[nv_clk_src_host]; + + if (kHz == 277000) { + info->clk = 0; + info->host_out = NVA3_HOST_277; + return 0; + } + + info->host_out = NVA3_HOST_CLK; + + ret = nva3_clk_info(&priv->base, 0x1d, kHz, info); + if (ret >= 0) + return 0; + return ret; +} + +int +nva3_clock_pre(struct nouveau_clock *clk, unsigned long *flags) +{ + struct nouveau_fifo *pfifo = nouveau_fifo(clk); + + /* halt and idle execution engines */ + nv_mask(clk, 0x020060, 0x00070000, 0x00000000); + nv_mask(clk, 0x002504, 0x00000001, 0x00000001); + /* Wait until the interrupt handler is finished */ + if (!nv_wait(clk, 0x000100, 0xffffffff, 0x00000000)) + return -EBUSY; + + if (pfifo) + pfifo->pause(pfifo, flags); + + if (!nv_wait(clk, 0x002504, 0x00000010, 0x00000010)) + return -EIO; + if (!nv_wait(clk, 0x00251c, 0x0000003f, 0x0000003f)) + return -EIO; + + return 0; +} + +void +nva3_clock_post(struct nouveau_clock *clk, unsigned long *flags) +{ + struct nouveau_fifo *pfifo = nouveau_fifo(clk); + + if (pfifo && flags) + pfifo->start(pfifo, flags); + + nv_mask(clk, 0x002504, 0x00000001, 0x00000000); + nv_mask(clk, 0x020060, 0x00070000, 0x00040000); +} + +static void +disable_clk_src(struct nva3_clock_priv *priv, u32 src) +{ + nv_mask(priv, src, 0x00000100, 0x00000000); + nv_mask(priv, src, 0x00000001, 0x00000000); +} + static void prog_pll(struct nva3_clock_priv *priv, int clk, u32 pll, int idx) { @@ -223,24 +344,35 @@ prog_pll(struct nva3_clock_priv *priv, int clk, u32 pll, int idx) const u32 src1 = 0x004160 + (clk * 4); const u32 ctrl = pll + 0; const u32 coef = pll + 4; + u32 bypass; if (info->pll) { - nv_mask(priv, src0, 0x00000101, 0x00000101); + /* Always start from a non-PLL clock */ + bypass = nv_rd32(priv, ctrl) & 0x00000008; + if (!bypass) { + nv_mask(priv, src1, 0x00000101, 0x00000101); + nv_mask(priv, ctrl, 0x00000008, 0x00000008); + udelay(20); + } + + nv_mask(priv, src0, 0x003f3141, 0x00000101 | info->clk); nv_wr32(priv, coef, info->pll); nv_mask(priv, ctrl, 0x00000015, 0x00000015); nv_mask(priv, ctrl, 0x00000010, 0x00000000); - nv_wait(priv, ctrl, 0x00020000, 0x00020000); + if (!nv_wait(priv, ctrl, 0x00020000, 0x00020000)) { + nv_mask(priv, ctrl, 0x00000010, 0x00000010); + nv_mask(priv, src0, 0x00000101, 0x00000000); + return; + } nv_mask(priv, ctrl, 0x00000010, 0x00000010); nv_mask(priv, ctrl, 0x00000008, 0x00000000); - nv_mask(priv, src1, 0x00000100, 0x00000000); - nv_mask(priv, src1, 0x00000001, 0x00000000); + disable_clk_src(priv, src1); } else { nv_mask(priv, src1, 0x003f3141, 0x00000101 | info->clk); nv_mask(priv, ctrl, 0x00000018, 0x00000018); udelay(20); nv_mask(priv, ctrl, 0x00000001, 0x00000000); - nv_mask(priv, src0, 0x00000100, 0x00000000); - nv_mask(priv, src0, 0x00000001, 0x00000000); + disable_clk_src(priv, src0); } } @@ -251,18 +383,72 @@ prog_clk(struct nva3_clock_priv *priv, int clk, int idx) nv_mask(priv, 0x004120 + (clk * 4), 0x003f3141, 0x00000101 | info->clk); } +static void +prog_host(struct nva3_clock_priv *priv) +{ + struct nva3_clock_info *info = &priv->eng[nv_clk_src_host]; + u32 hsrc = (nv_rd32(priv, 0xc040)); + + switch (info->host_out) { + case NVA3_HOST_277: + if ((hsrc & 0x30000000) == 0) { + nv_wr32(priv, 0xc040, hsrc | 0x20000000); + disable_clk_src(priv, 0x4194); + } + break; + case NVA3_HOST_CLK: + prog_clk(priv, 0x1d, nv_clk_src_host); + if ((hsrc & 0x30000000) >= 0x20000000) { + nv_wr32(priv, 0xc040, hsrc & ~0x30000000); + } + break; + default: + break; + } + + /* This seems to be a clock gating factor on idle, always set to 64 */ + nv_wr32(priv, 0xc044, 0x3e); +} + +static void +prog_core(struct nva3_clock_priv *priv, int idx) +{ + struct nva3_clock_info *info = &priv->eng[idx]; + u32 fb_delay = nv_rd32(priv, 0x10002c); + + if (fb_delay < info->fb_delay) + nv_wr32(priv, 0x10002c, info->fb_delay); + + prog_pll(priv, 0x00, 0x004200, idx); + + if (fb_delay > info->fb_delay) + nv_wr32(priv, 0x10002c, info->fb_delay); +} + static int nva3_clock_calc(struct nouveau_clock *clk, struct nouveau_cstate *cstate) { struct nva3_clock_priv *priv = (void *)clk; + struct nva3_clock_info *core = &priv->eng[nv_clk_src_core]; int ret; if ((ret = calc_clk(priv, cstate, 0x10, 0x4200, nv_clk_src_core)) || (ret = calc_clk(priv, cstate, 0x11, 0x4220, nv_clk_src_shader)) || (ret = calc_clk(priv, cstate, 0x20, 0x0000, nv_clk_src_disp)) || - (ret = calc_clk(priv, cstate, 0x21, 0x0000, nv_clk_src_vdec))) + (ret = calc_clk(priv, cstate, 0x21, 0x0000, nv_clk_src_vdec)) || + (ret = calc_host(priv, cstate))) return ret; + /* XXX: Should be reading the highest bit in the VBIOS clock to decide + * whether to use a PLL or not... but using a PLL defeats the purpose */ + if (core->pll) { + ret = nva3_clk_info(clk, 0x10, + cstate->domain[nv_clk_src_core_intm], + &priv->eng[nv_clk_src_core_intm]); + if (ret < 0) + return ret; + } + return 0; } @@ -270,11 +456,31 @@ static int nva3_clock_prog(struct nouveau_clock *clk) { struct nva3_clock_priv *priv = (void *)clk; - prog_pll(priv, 0x00, 0x004200, nv_clk_src_core); + struct nva3_clock_info *core = &priv->eng[nv_clk_src_core]; + int ret = 0; + unsigned long flags; + unsigned long *f = &flags; + + ret = nva3_clock_pre(clk, f); + if (ret) + goto out; + + if (core->pll) + prog_core(priv, nv_clk_src_core_intm); + + prog_core(priv, nv_clk_src_core); prog_pll(priv, 0x01, 0x004220, nv_clk_src_shader); prog_clk(priv, 0x20, nv_clk_src_disp); prog_clk(priv, 0x21, nv_clk_src_vdec); - return 0; + prog_host(priv); + +out: + if (ret == -EBUSY) + f = NULL; + + nva3_clock_post(clk, f); + + return ret; } static void @@ -284,13 +490,14 @@ nva3_clock_tidy(struct nouveau_clock *clk) static struct nouveau_clocks nva3_domain[] = { - { nv_clk_src_crystal, 0xff }, - { nv_clk_src_href , 0xff }, - { nv_clk_src_core , 0x00, 0, "core", 1000 }, - { nv_clk_src_shader , 0x01, 0, "shader", 1000 }, - { nv_clk_src_mem , 0x02, 0, "memory", 1000 }, - { nv_clk_src_vdec , 0x03 }, - { nv_clk_src_disp , 0x04 }, + { nv_clk_src_crystal , 0xff }, + { nv_clk_src_core , 0x00, 0, "core", 1000 }, + { nv_clk_src_shader , 0x01, 0, "shader", 1000 }, + { nv_clk_src_mem , 0x02, 0, "memory", 1000 }, + { nv_clk_src_vdec , 0x03 }, + { nv_clk_src_disp , 0x04 }, + { nv_clk_src_host , 0x05 }, + { nv_clk_src_core_intm, 0x06 }, { nv_clk_src_max } }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.h b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.h index 6229a509b42e77a88777245fb5c3a2855e9baa2b..a45a1038b12fbe8f6ffec100734b1a7d40e95b57 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.h +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.h @@ -6,9 +6,15 @@ struct nva3_clock_info { u32 clk; u32 pll; + enum { + NVA3_HOST_277, + NVA3_HOST_CLK, + } host_out; + u32 fb_delay; }; -int nva3_clock_info(struct nouveau_clock *, int, u32, u32, +int nva3_pll_info(struct nouveau_clock *, int, u32, u32, struct nva3_clock_info *); - +int nva3_clock_pre(struct nouveau_clock *clk, unsigned long *flags); +void nva3_clock_post(struct nouveau_clock *clk, unsigned long *flags); #endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c index 74e19731b1b7db97fc942a70d6e7289a25083a93..54aeab8005a0042ec0ae4dccfd5908c7bef95843 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c @@ -28,6 +28,7 @@ #include #include +#include "nva3.h" #include "pll.h" struct nvaa_clock_priv { @@ -299,25 +300,14 @@ static int nvaa_clock_prog(struct nouveau_clock *clk) { struct nvaa_clock_priv *priv = (void *)clk; - struct nouveau_fifo *pfifo = nouveau_fifo(clk); + u32 pllmask = 0, mast; unsigned long flags; - u32 pllmask = 0, mast, ptherm_gate; - int ret = -EBUSY; - - /* halt and idle execution engines */ - ptherm_gate = nv_mask(clk, 0x020060, 0x00070000, 0x00000000); - nv_mask(clk, 0x002504, 0x00000001, 0x00000001); - /* Wait until the interrupt handler is finished */ - if (!nv_wait(clk, 0x000100, 0xffffffff, 0x00000000)) - goto resume; - - if (pfifo) - pfifo->pause(pfifo, &flags); + unsigned long *f = &flags; + int ret = 0; - if (!nv_wait(clk, 0x002504, 0x00000010, 0x00000010)) - goto resume; - if (!nv_wait(clk, 0x00251c, 0x0000003f, 0x0000003f)) - goto resume; + ret = nva3_clock_pre(clk, f); + if (ret) + goto out; /* First switch to safe clocks: href */ mast = nv_mask(clk, 0xc054, 0x03400e70, 0x03400640); @@ -375,15 +365,8 @@ nvaa_clock_prog(struct nouveau_clock *clk) } nv_wr32(clk, 0xc054, mast); - ret = 0; resume: - if (pfifo) - pfifo->start(pfifo, &flags); - - nv_mask(clk, 0x002504, 0x00000001, 0x00000000); - nv_wr32(clk, 0x020060, ptherm_gate); - /* Disable some PLLs and dividers when unused */ if (priv->csrc != nv_clk_src_core) { nv_wr32(clk, 0x4040, 0x00000000); @@ -395,6 +378,12 @@ nvaa_clock_prog(struct nouveau_clock *clk) nv_mask(clk, 0x4020, 0x80000000, 0x00000000); } +out: + if (ret == -EBUSY) + f = NULL; + + nva3_clock_post(clk, f); + return ret; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/fbmem.h b/drivers/gpu/drm/nouveau/core/subdev/devinit/fbmem.h index 4fe49cf4c99a37eaf32d79eecf029501cac353f7..6103484fea724da49f8984529b12d5f7a6f8b371 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/devinit/fbmem.h +++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/fbmem.h @@ -26,22 +26,8 @@ #include -#define NV04_PFB_BOOT_0 0x00100000 -# define NV04_PFB_BOOT_0_RAM_AMOUNT 0x00000003 -# define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB 0x00000000 -# define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB 0x00000001 -# define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB 0x00000002 -# define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB 0x00000003 -# define NV04_PFB_BOOT_0_RAM_WIDTH_128 0x00000004 -# define NV04_PFB_BOOT_0_RAM_TYPE 0x00000028 -# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x00000000 -# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x00000008 -# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x00000010 -# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x00000018 -# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x00000020 -# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x00000028 -# define NV04_PFB_BOOT_0_UMA_ENABLE 0x00000100 -# define NV04_PFB_BOOT_0_UMA_SIZE 0x0000f000 +#include + #define NV04_PFB_DEBUG_0 0x00100080 # define NV04_PFB_DEBUG_0_PAGE_MODE 0x00000001 # define NV04_PFB_DEBUG_0_REFRESH_OFF 0x00000010 diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/gddr5.c b/drivers/gpu/drm/nouveau/core/subdev/fb/gddr5.c index 66fe959b4f7431dd2cc1b9ce6520263560fb769f..7fbbe05d5c608d7037b07e9e9690f0035ba6dfa3 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/gddr5.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/gddr5.c @@ -40,7 +40,7 @@ nouveau_gddr5_calc(struct nouveau_ram *ram, bool nuts) int WL, CL, WR, at[2], dt, ds; int rq = ram->freq < 1000000; /* XXX */ - switch (ram->ramcfg.version) { + switch (ram->next->bios.ramcfg_ver) { case 0x11: pd = ram->next->bios.ramcfg_11_01_80; lf = ram->next->bios.ramcfg_11_01_40; @@ -54,7 +54,7 @@ nouveau_gddr5_calc(struct nouveau_ram *ram, bool nuts) return -ENOSYS; } - switch (ram->timing.version) { + switch (ram->next->bios.timing_ver) { case 0x20: WL = (ram->next->bios.timing[1] & 0x00000f80) >> 7; CL = (ram->next->bios.timing[1] & 0x0000001f); diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c index f003c1b1893f48c1a115e55e835a749d98b41207..2209ade63339ed93645f600bc5d0eb3e7626e2ba 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c @@ -45,7 +45,7 @@ nv20_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, { u32 tiles = DIV_ROUND_UP(size, 0x40); u32 tags = round_up(tiles / pfb->ram->parts, 0x40); - if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { + if (!nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { if (!(flags & 2)) tile->zcomp = 0x00000000; /* Z16 */ else tile->zcomp = 0x04000000; /* Z24S8 */ tile->zcomp |= tile->tag->offset; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c index f34f4223210b8552bc143f096bd8117ca216e158..e2a66c355c50dff411098012dc808a085905dd1e 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c @@ -32,7 +32,7 @@ nv25_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, { u32 tiles = DIV_ROUND_UP(size, 0x40); u32 tags = round_up(tiles / pfb->ram->parts, 0x40); - if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { + if (!nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { if (!(flags & 2)) tile->zcomp = 0x00100000; /* Z16 */ else tile->zcomp = 0x00200000; /* Z24S8 */ tile->zcomp |= tile->tag->offset; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c index 69093f7151f01d168117098983a6812e9b3ff965..cbec402ba5b92861f0f75dc48e781aaacd2212ec 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c @@ -51,7 +51,7 @@ nv30_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, { u32 tiles = DIV_ROUND_UP(size, 0x40); u32 tags = round_up(tiles / pfb->ram->parts, 0x40); - if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { + if (!nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { if (flags & 2) tile->zcomp |= 0x01000000; /* Z16 */ else tile->zcomp |= 0x02000000; /* Z24S8 */ tile->zcomp |= ((tile->tag->offset ) >> 6); diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c index 161b06e8fc3f75f31567054c3f68117e0691e93e..b2cf8c69fb2ee67e9d50c973adf159fd6a16d431 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c @@ -32,7 +32,7 @@ nv35_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, { u32 tiles = DIV_ROUND_UP(size, 0x40); u32 tags = round_up(tiles / pfb->ram->parts, 0x40); - if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { + if (!nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { if (flags & 2) tile->zcomp |= 0x04000000; /* Z16 */ else tile->zcomp |= 0x08000000; /* Z24S8 */ tile->zcomp |= ((tile->tag->offset ) >> 6); diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c index 2dd3d0aab6bb6434fdb780c1152f71453aa22324..b4cdae2a3b2f192bcbd4ee13fa45bef459435311 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c @@ -32,7 +32,7 @@ nv36_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, { u32 tiles = DIV_ROUND_UP(size, 0x40); u32 tags = round_up(tiles / pfb->ram->parts, 0x40); - if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { + if (!nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { if (flags & 2) tile->zcomp |= 0x10000000; /* Z16 */ else tile->zcomp |= 0x20000000; /* Z24S8 */ tile->zcomp |= ((tile->tag->offset ) >> 6); diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c index 95a115ab0c860bc591f63033f6305c7f15fb0b59..52814258c21279f8fe88bd3b316dfac0fa4a95e3 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c @@ -33,7 +33,7 @@ nv40_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, u32 tiles = DIV_ROUND_UP(size, 0x80); u32 tags = round_up(tiles / pfb->ram->parts, 0x100); if ( (flags & 2) && - !nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { + !nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) { tile->zcomp = 0x28000000; /* Z24S8_SPLIT_GRAD */ tile->zcomp |= ((tile->tag->offset ) >> 8); tile->zcomp |= ((tile->tag->offset + tags - 1) >> 8) << 13; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h b/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h index 82273f832e42ef1fa5d7993e8db299c6e74be499..60322e906dd403c5b2d206c4590605f612d4ba0d 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h @@ -35,6 +35,7 @@ extern struct nouveau_oclass nve0_ram_oclass; extern struct nouveau_oclass gk20a_ram_oclass; extern struct nouveau_oclass gm107_ram_oclass; +int nouveau_sddr2_calc(struct nouveau_ram *ram); int nouveau_sddr3_calc(struct nouveau_ram *ram); int nouveau_gddr5_calc(struct nouveau_ram *ram, bool nuts); diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h b/drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h index 2af9cfd2c60fd7667b54037fa9254224b065bd50..d1fbbe4b00a2a4ef1eeccef2f43b00cfa02c096d 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h @@ -12,16 +12,32 @@ struct ramfuc { struct ramfuc_reg { int sequence; bool force; - u32 addr[2]; + u32 addr; + u32 stride; /* in bytes */ + u32 mask; u32 data; }; +static inline struct ramfuc_reg +ramfuc_stride(u32 addr, u32 stride, u32 mask) +{ + return (struct ramfuc_reg) { + .sequence = 0, + .addr = addr, + .stride = stride, + .mask = mask, + .data = 0xdeadbeef, + }; +} + static inline struct ramfuc_reg ramfuc_reg2(u32 addr1, u32 addr2) { return (struct ramfuc_reg) { .sequence = 0, - .addr = { addr1, addr2 }, + .addr = addr1, + .stride = addr2 - addr1, + .mask = 0x3, .data = 0xdeadbeef, }; } @@ -29,7 +45,13 @@ ramfuc_reg2(u32 addr1, u32 addr2) static noinline struct ramfuc_reg ramfuc_reg(u32 addr) { - return ramfuc_reg2(addr, addr); + return (struct ramfuc_reg) { + .sequence = 0, + .addr = addr, + .stride = 0, + .mask = 0x1, + .data = 0xdeadbeef, + }; } static inline int @@ -62,18 +84,25 @@ static inline u32 ramfuc_rd32(struct ramfuc *ram, struct ramfuc_reg *reg) { if (reg->sequence != ram->sequence) - reg->data = nv_rd32(ram->pfb, reg->addr[0]); + reg->data = nv_rd32(ram->pfb, reg->addr); return reg->data; } static inline void ramfuc_wr32(struct ramfuc *ram, struct ramfuc_reg *reg, u32 data) { + unsigned int mask, off = 0; + reg->sequence = ram->sequence; reg->data = data; - if (reg->addr[0] != reg->addr[1]) - nouveau_memx_wr32(ram->memx, reg->addr[1], reg->data); - nouveau_memx_wr32(ram->memx, reg->addr[0], reg->data); + + for (mask = reg->mask; mask > 0; mask = (mask & ~1) >> 1) { + if (mask & 1) { + nouveau_memx_wr32(ram->memx, reg->addr+off, reg->data); + } + + off += reg->stride; + } } static inline void @@ -105,14 +134,35 @@ ramfuc_nsec(struct ramfuc *ram, u32 nsec) nouveau_memx_nsec(ram->memx, nsec); } -#define ram_init(s,p) ramfuc_init(&(s)->base, (p)) -#define ram_exec(s,e) ramfuc_exec(&(s)->base, (e)) -#define ram_have(s,r) ((s)->r_##r.addr[0] != 0x000000) -#define ram_rd32(s,r) ramfuc_rd32(&(s)->base, &(s)->r_##r) -#define ram_wr32(s,r,d) ramfuc_wr32(&(s)->base, &(s)->r_##r, (d)) -#define ram_nuke(s,r) ramfuc_nuke(&(s)->base, &(s)->r_##r) -#define ram_mask(s,r,m,d) ramfuc_mask(&(s)->base, &(s)->r_##r, (m), (d)) -#define ram_wait(s,r,m,d,n) ramfuc_wait(&(s)->base, (r), (m), (d), (n)) -#define ram_nsec(s,n) ramfuc_nsec(&(s)->base, (n)) +static inline void +ramfuc_wait_vblank(struct ramfuc *ram) +{ + nouveau_memx_wait_vblank(ram->memx); +} + +static inline void +ramfuc_block(struct ramfuc *ram) +{ + nouveau_memx_block(ram->memx); +} + +static inline void +ramfuc_unblock(struct ramfuc *ram) +{ + nouveau_memx_unblock(ram->memx); +} + +#define ram_init(s,p) ramfuc_init(&(s)->base, (p)) +#define ram_exec(s,e) ramfuc_exec(&(s)->base, (e)) +#define ram_have(s,r) ((s)->r_##r.addr != 0x000000) +#define ram_rd32(s,r) ramfuc_rd32(&(s)->base, &(s)->r_##r) +#define ram_wr32(s,r,d) ramfuc_wr32(&(s)->base, &(s)->r_##r, (d)) +#define ram_nuke(s,r) ramfuc_nuke(&(s)->base, &(s)->r_##r) +#define ram_mask(s,r,m,d) ramfuc_mask(&(s)->base, &(s)->r_##r, (m), (d)) +#define ram_wait(s,r,m,d,n) ramfuc_wait(&(s)->base, (r), (m), (d), (n)) +#define ram_nsec(s,n) ramfuc_nsec(&(s)->base, (n)) +#define ram_wait_vblank(s) ramfuc_wait_vblank(&(s)->base) +#define ram_block(s) ramfuc_block(&(s)->base) +#define ram_unblock(s) ramfuc_unblock(&(s)->base) #endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv04.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv04.c index e781080d33276132722c4b382da880819f9459ec..1972268d14104d284aed6cf07a12b75147f01317 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv04.c @@ -22,22 +22,7 @@ * Authors: Ben Skeggs */ -#define NV04_PFB_BOOT_0 0x00100000 -# define NV04_PFB_BOOT_0_RAM_AMOUNT 0x00000003 -# define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB 0x00000000 -# define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB 0x00000001 -# define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB 0x00000002 -# define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB 0x00000003 -# define NV04_PFB_BOOT_0_RAM_WIDTH_128 0x00000004 -# define NV04_PFB_BOOT_0_RAM_TYPE 0x00000028 -# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x00000000 -# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x00000008 -# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x00000010 -# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x00000018 -# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x00000020 -# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x00000028 -# define NV04_PFB_BOOT_0_UMA_ENABLE 0x00000100 -# define NV04_PFB_BOOT_0_UMA_SIZE 0x0000f000 +#include #include "priv.h" diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c index e5d12c24cc43e132df49142ba23256c7e99599f5..64a983c9662576bad70207aad209564b32cd6b4f 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c @@ -280,7 +280,7 @@ nv50_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, if (align == 16) { int n = (max >> 4) * comp; - ret = nouveau_mm_head(tags, 1, n, n, 1, &mem->tag); + ret = nouveau_mm_head(tags, 0, 1, n, n, 1, &mem->tag); if (ret) mem->tag = NULL; } @@ -296,9 +296,9 @@ nv50_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, type = nv50_fb_memtype[type]; do { if (back) - ret = nouveau_mm_tail(heap, type, max, min, align, &r); + ret = nouveau_mm_tail(heap, 0, type, max, min, align, &r); else - ret = nouveau_mm_head(heap, type, max, min, align, &r); + ret = nouveau_mm_head(heap, 0, type, max, min, align, &r); if (ret) { mutex_unlock(&pfb->base.mutex); pfb->ram->put(pfb, &mem); @@ -319,27 +319,22 @@ nv50_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, static u32 nv50_fb_vram_rblock(struct nouveau_fb *pfb, struct nouveau_ram *ram) { - int i, parts, colbits, rowbitsa, rowbitsb, banks; + int colbits, rowbitsa, rowbitsb, banks; u64 rowsize, predicted; - u32 r0, r4, rt, ru, rblock_size; + u32 r0, r4, rt, rblock_size; r0 = nv_rd32(pfb, 0x100200); r4 = nv_rd32(pfb, 0x100204); rt = nv_rd32(pfb, 0x100250); - ru = nv_rd32(pfb, 0x001540); - nv_debug(pfb, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", r0, r4, rt, ru); - - for (i = 0, parts = 0; i < 8; i++) { - if (ru & (0x00010000 << i)) - parts++; - } + nv_debug(pfb, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", r0, r4, rt, + nv_rd32(pfb, 0x001540)); colbits = (r4 & 0x0000f000) >> 12; rowbitsa = ((r4 & 0x000f0000) >> 16) + 8; rowbitsb = ((r4 & 0x00f00000) >> 20) + 8; banks = 1 << (((r4 & 0x03000000) >> 24) + 2); - rowsize = parts * banks * (1 << colbits) * 8; + rowsize = ram->parts * banks * (1 << colbits) * 8; predicted = rowsize << rowbitsa; if (r0 & 0x00000004) predicted += rowsize << rowbitsb; @@ -376,6 +371,9 @@ nv50_ram_create_(struct nouveau_object *parent, struct nouveau_object *engine, ram->size = nv_rd32(pfb, 0x10020c); ram->size = (ram->size & 0xffffff00) | ((ram->size & 0x000000ff) << 32); + ram->part_mask = (nv_rd32(pfb, 0x001540) & 0x00ff0000) >> 16; + ram->parts = hweight8(ram->part_mask); + switch (nv_rd32(pfb, 0x100714) & 0x00000007) { case 0: ram->type = NV_MEM_TYPE_DDR1; break; case 1: diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c index 8076fb195dd514569466ae95d1ee12ef929ffab9..3601deca0bd5d01dd81920fc789aa5a96e148950 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c @@ -79,20 +79,27 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq) struct nva3_ram *ram = (void *)pfb->ram; struct nva3_ramfuc *fuc = &ram->fuc; struct nva3_clock_info mclk; - u8 ver, cnt, len, strap; + struct nouveau_ram_data *next; + u8 ver, hdr, cnt, len, strap; u32 data; - struct { - u32 data; - u8 size; - } rammap, ramcfg, timing; u32 r004018, r100760, ctrl; u32 unk714, unk718, unk71c; - int ret; + int ret, i; + + next = &ram->base.target; + next->freq = freq; + ram->base.next = next; /* lookup memory config data relevant to the target frequency */ - rammap.data = nvbios_rammapEm(bios, freq / 1000, &ver, &rammap.size, - &cnt, &ramcfg.size); - if (!rammap.data || ver != 0x10 || rammap.size < 0x0e) { + i = 0; + while ((data = nvbios_rammapEp(bios, i++, &ver, &hdr, &cnt, &len, + &next->bios))) { + if (freq / 1000 >= next->bios.rammap_min && + freq / 1000 <= next->bios.rammap_max) + break; + } + + if (!data || ver != 0x10 || hdr < 0x0e) { nv_error(pfb, "invalid/missing rammap entry\n"); return -EINVAL; } @@ -104,26 +111,25 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq) return -EINVAL; } - ramcfg.data = rammap.data + rammap.size + (strap * ramcfg.size); - if (!ramcfg.data || ver != 0x10 || ramcfg.size < 0x0e) { + data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, strap, + &ver, &hdr, &next->bios); + if (!data || ver != 0x10 || hdr < 0x0e) { nv_error(pfb, "invalid/missing ramcfg entry\n"); return -EINVAL; } /* lookup memory timings, if bios says they're present */ - strap = nv_ro08(bios, ramcfg.data + 0x01); - if (strap != 0xff) { - timing.data = nvbios_timingEe(bios, strap, &ver, &timing.size, - &cnt, &len); - if (!timing.data || ver != 0x10 || timing.size < 0x19) { + if (next->bios.ramcfg_timing != 0xff) { + data = nvbios_timingEp(bios, next->bios.ramcfg_timing, + &ver, &hdr, &cnt, &len, + &next->bios); + if (!data || ver != 0x10 || hdr < 0x19) { nv_error(pfb, "invalid/missing timing entry\n"); return -EINVAL; } - } else { - timing.data = 0; } - ret = nva3_clock_info(nouveau_clock(pfb), 0x12, 0x4000, freq, &mclk); + ret = nva3_pll_info(nouveau_clock(pfb), 0x12, 0x4000, freq, &mclk); if (ret < 0) { nv_error(pfb, "failed mclk calculation\n"); return ret; @@ -163,17 +169,17 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq) ram_mask(fuc, 0x004168, 0x003f3141, ctrl); } - if ( (nv_ro08(bios, ramcfg.data + 0x02) & 0x10)) { + if (next->bios.ramcfg_10_02_10) { ram_mask(fuc, 0x111104, 0x00000600, 0x00000000); } else { ram_mask(fuc, 0x111100, 0x40000000, 0x40000000); ram_mask(fuc, 0x111104, 0x00000180, 0x00000000); } - if (!(nv_ro08(bios, rammap.data + 0x04) & 0x02)) + if (!next->bios.rammap_10_04_02) ram_mask(fuc, 0x100200, 0x00000800, 0x00000000); ram_wr32(fuc, 0x611200, 0x00003300); - if (!(nv_ro08(bios, ramcfg.data + 0x02) & 0x10)) + if (!next->bios.ramcfg_10_02_10) ram_wr32(fuc, 0x111100, 0x4c020000); /*XXX*/ ram_wr32(fuc, 0x1002d4, 0x00000001); @@ -202,17 +208,16 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq) ram_wr32(fuc, 0x004018, 0x0000d000 | r004018); } - if ( (nv_ro08(bios, rammap.data + 0x04) & 0x08)) { - u32 unk5a0 = (nv_ro16(bios, ramcfg.data + 0x05) << 8) | - nv_ro08(bios, ramcfg.data + 0x05); - u32 unk5a4 = (nv_ro16(bios, ramcfg.data + 0x07)); - u32 unk804 = (nv_ro08(bios, ramcfg.data + 0x09) & 0xf0) << 16 | - (nv_ro08(bios, ramcfg.data + 0x03) & 0x0f) << 16 | - (nv_ro08(bios, ramcfg.data + 0x09) & 0x0f) | - 0x80000000; - ram_wr32(fuc, 0x1005a0, unk5a0); - ram_wr32(fuc, 0x1005a4, unk5a4); - ram_wr32(fuc, 0x10f804, unk804); + if (next->bios.rammap_10_04_08) { + ram_wr32(fuc, 0x1005a0, next->bios.ramcfg_10_06 << 16 | + next->bios.ramcfg_10_05 << 8 | + next->bios.ramcfg_10_05); + ram_wr32(fuc, 0x1005a4, next->bios.ramcfg_10_08 << 8 | + next->bios.ramcfg_10_07); + ram_wr32(fuc, 0x10f804, next->bios.ramcfg_10_09_f0 << 20 | + next->bios.ramcfg_10_03_0f << 16 | + next->bios.ramcfg_10_09_0f | + 0x80000000); ram_mask(fuc, 0x10053c, 0x00001000, 0x00000000); } else { ram_mask(fuc, 0x10053c, 0x00001000, 0x00001000); @@ -250,27 +255,26 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq) ram_mask(fuc, 0x100220[0], 0x00000000, 0x00000000); ram_mask(fuc, 0x100220[8], 0x00000000, 0x00000000); - data = (nv_ro08(bios, ramcfg.data + 0x02) & 0x08) ? 0x00000000 : 0x00001000; - ram_mask(fuc, 0x100200, 0x00001000, data); + ram_mask(fuc, 0x100200, 0x00001000, !next->bios.ramcfg_10_02_08 << 12); unk714 = ram_rd32(fuc, 0x100714) & ~0xf0000010; unk718 = ram_rd32(fuc, 0x100718) & ~0x00000100; unk71c = ram_rd32(fuc, 0x10071c) & ~0x00000100; - if ( (nv_ro08(bios, ramcfg.data + 0x02) & 0x20)) + if (next->bios.ramcfg_10_02_20) unk714 |= 0xf0000000; - if (!(nv_ro08(bios, ramcfg.data + 0x02) & 0x04)) + if (!next->bios.ramcfg_10_02_04) unk714 |= 0x00000010; ram_wr32(fuc, 0x100714, unk714); - if (nv_ro08(bios, ramcfg.data + 0x02) & 0x01) + if (next->bios.ramcfg_10_02_01) unk71c |= 0x00000100; ram_wr32(fuc, 0x10071c, unk71c); - if (nv_ro08(bios, ramcfg.data + 0x02) & 0x02) + if (next->bios.ramcfg_10_02_02) unk718 |= 0x00000100; ram_wr32(fuc, 0x100718, unk718); - if (nv_ro08(bios, ramcfg.data + 0x02) & 0x10) + if (next->bios.ramcfg_10_02_10) ram_wr32(fuc, 0x111100, 0x48000000); /*XXX*/ ram_mask(fuc, mr[0], 0x100, 0x100); @@ -282,9 +286,9 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq) ram_nsec(fuc, 12000); ram_wr32(fuc, 0x611200, 0x00003330); - if ( (nv_ro08(bios, rammap.data + 0x04) & 0x02)) + if (next->bios.rammap_10_04_02) ram_mask(fuc, 0x100200, 0x00000800, 0x00000800); - if ( (nv_ro08(bios, ramcfg.data + 0x02) & 0x10)) { + if (next->bios.ramcfg_10_02_10) { ram_mask(fuc, 0x111104, 0x00000180, 0x00000180); ram_mask(fuc, 0x111100, 0x40000000, 0x00000000); } else { @@ -404,11 +408,11 @@ nva3_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine, ram->fuc.r_0x100714 = ramfuc_reg(0x100714); ram->fuc.r_0x100718 = ramfuc_reg(0x100718); ram->fuc.r_0x10071c = ramfuc_reg(0x10071c); - ram->fuc.r_0x100760 = ramfuc_reg(0x100760); - ram->fuc.r_0x1007a0 = ramfuc_reg(0x1007a0); - ram->fuc.r_0x1007e0 = ramfuc_reg(0x1007e0); + ram->fuc.r_0x100760 = ramfuc_stride(0x100760, 4, ram->base.part_mask); + ram->fuc.r_0x1007a0 = ramfuc_stride(0x1007a0, 4, ram->base.part_mask); + ram->fuc.r_0x1007e0 = ramfuc_stride(0x1007e0, 4, ram->base.part_mask); ram->fuc.r_0x10f804 = ramfuc_reg(0x10f804); - ram->fuc.r_0x1110e0 = ramfuc_reg(0x1110e0); + ram->fuc.r_0x1110e0 = ramfuc_stride(0x1110e0, 4, ram->base.part_mask); ram->fuc.r_0x111100 = ramfuc_reg(0x111100); ram->fuc.r_0x111104 = ramfuc_reg(0x111104); ram->fuc.r_0x611200 = ramfuc_reg(0x611200); diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c index 2b284b19276330aabd6b584c55b21eb727232662..735cb9580abe88330efe78b8718378b619d4d66d 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c @@ -133,6 +133,7 @@ nvc0_ram_calc(struct nouveau_fb *pfb, u32 freq) struct nouveau_bios *bios = nouveau_bios(pfb); struct nvc0_ram *ram = (void *)pfb->ram; struct nvc0_ramfuc *fuc = &ram->fuc; + struct nvbios_ramcfg cfg; u8 ver, cnt, len, strap; struct { u32 data; @@ -145,7 +146,7 @@ nvc0_ram_calc(struct nouveau_fb *pfb, u32 freq) /* lookup memory config data relevant to the target frequency */ rammap.data = nvbios_rammapEm(bios, freq / 1000, &ver, &rammap.size, - &cnt, &ramcfg.size); + &cnt, &ramcfg.size, &cfg); if (!rammap.data || ver != 0x10 || rammap.size < 0x0e) { nv_error(pfb, "invalid/missing rammap entry\n"); return -EINVAL; @@ -483,9 +484,9 @@ nvc0_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, do { if (back) - ret = nouveau_mm_tail(mm, 1, size, ncmin, align, &r); + ret = nouveau_mm_tail(mm, 0, 1, size, ncmin, align, &r); else - ret = nouveau_mm_head(mm, 1, size, ncmin, align, &r); + ret = nouveau_mm_head(mm, 0, 1, size, ncmin, align, &r); if (ret) { mutex_unlock(&pfb->base.mutex); pfb->ram->put(pfb, &mem); @@ -562,7 +563,7 @@ nvc0_ram_create_(struct nouveau_object *parent, struct nouveau_object *engine, offset = (0x0200000000ULL >> 12) + (bsize << 8); length = (ram->size >> 12) - ((bsize * parts) << 8) - rsvd_tail; - ret = nouveau_mm_init(&pfb->vram, offset, length, 0); + ret = nouveau_mm_init(&pfb->vram, offset, length, 1); if (ret) nouveau_mm_fini(&pfb->vram); } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c index c5b46e3023199299362f68181ef98ec9f4db7644..6bae474abb4476ed46510b74000c68b457e817f4 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include @@ -41,14 +43,6 @@ #include "ramfuc.h" -/* binary driver only executes this path if the condition (a) is true - * for any configuration (combination of rammap+ramcfg+timing) that - * can be reached on a given card. for now, we will execute the branch - * unconditionally in the hope that a "false everywhere" in the bios - * tables doesn't actually mean "don't touch this". - */ -#define NOTE00(a) 1 - struct nve0_ramfuc { struct ramfuc base; @@ -134,10 +128,12 @@ struct nve0_ram { struct nouveau_ram base; struct nve0_ramfuc fuc; + struct list_head cfg; u32 parts; u32 pmask; u32 pnuts; + struct nvbios_ramcfg diff; int from; int mode; int N1, fN1, M1, P1; @@ -241,7 +237,7 @@ nve0_ram_nuts(struct nve0_ram *ram, struct ramfuc_reg *reg, { struct nve0_fb_priv *priv = (void *)nouveau_fb(ram); struct ramfuc *fuc = &ram->fuc.base; - u32 addr = 0x110000 + (reg->addr[0] & 0xfff); + u32 addr = 0x110000 + (reg->addr & 0xfff); u32 mask = _mask | _copy; u32 data = (_data & _mask) | (reg->data & _copy); u32 i; @@ -268,6 +264,7 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) u32 mask, data; ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000); + ram_block(fuc); ram_wr32(fuc, 0x62c000, 0x0f0f0000); /* MR1: turn termination on early, for some reason.. */ @@ -478,7 +475,7 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) ram_mask(fuc, 0x10f2e8, 0xffffffff, next->bios.timing[9]); data = mask = 0x00000000; - if (NOTE00(ramcfg_08_20)) { + if (ram->diff.ramcfg_11_08_20) { if (next->bios.ramcfg_11_08_20) data |= 0x01000000; mask |= 0x01000000; @@ -486,11 +483,11 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) ram_mask(fuc, 0x10f200, mask, data); data = mask = 0x00000000; - if (NOTE00(ramcfg_02_03 != 0)) { + if (ram->diff.ramcfg_11_02_03) { data |= next->bios.ramcfg_11_02_03 << 8; mask |= 0x00000300; } - if (NOTE00(ramcfg_01_10)) { + if (ram->diff.ramcfg_11_01_10) { if (next->bios.ramcfg_11_01_10) data |= 0x70000000; mask |= 0x70000000; @@ -498,11 +495,11 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) ram_mask(fuc, 0x10f604, mask, data); data = mask = 0x00000000; - if (NOTE00(timing_30_07 != 0)) { + if (ram->diff.timing_20_30_07) { data |= next->bios.timing_20_30_07 << 28; mask |= 0x70000000; } - if (NOTE00(ramcfg_01_01)) { + if (ram->diff.ramcfg_11_01_01) { if (next->bios.ramcfg_11_01_01) data |= 0x00000100; mask |= 0x00000100; @@ -510,11 +507,11 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) ram_mask(fuc, 0x10f614, mask, data); data = mask = 0x00000000; - if (NOTE00(timing_30_07 != 0)) { + if (ram->diff.timing_20_30_07) { data |= next->bios.timing_20_30_07 << 28; mask |= 0x70000000; } - if (NOTE00(ramcfg_01_02)) { + if (ram->diff.ramcfg_11_01_02) { if (next->bios.ramcfg_11_01_02) data |= 0x00000100; mask |= 0x00000100; @@ -548,11 +545,11 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) ram_wr32(fuc, 0x10f870, 0x11111111 * next->bios.ramcfg_11_03_0f); data = mask = 0x00000000; - if (NOTE00(ramcfg_02_03 != 0)) { + if (ram->diff.ramcfg_11_02_03) { data |= next->bios.ramcfg_11_02_03; mask |= 0x00000003; } - if (NOTE00(ramcfg_01_10)) { + if (ram->diff.ramcfg_11_01_10) { if (next->bios.ramcfg_11_01_10) data |= 0x00000004; mask |= 0x00000004; @@ -666,6 +663,7 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq) if (next->bios.ramcfg_11_07_02) nve0_ram_train(fuc, 0x80020000, 0x01000000); + ram_unblock(fuc); ram_wr32(fuc, 0x62c000, 0x0f0f0f00); if (next->bios.rammap_11_08_01) @@ -695,6 +693,7 @@ nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq) u32 mask, data; ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000); + ram_block(fuc); ram_wr32(fuc, 0x62c000, 0x0f0f0000); if (vc == 1 && ram_have(fuc, gpio2E)) { @@ -917,6 +916,7 @@ nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq) ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000); ram_nsec(fuc, 1000); + ram_unblock(fuc); ram_wr32(fuc, 0x62c000, 0x0f0f0f00); if (next->bios.rammap_11_08_01) @@ -932,58 +932,24 @@ nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq) ******************************************************************************/ static int -nve0_ram_calc_data(struct nouveau_fb *pfb, u32 freq, +nve0_ram_calc_data(struct nouveau_fb *pfb, u32 khz, struct nouveau_ram_data *data) { - struct nouveau_bios *bios = nouveau_bios(pfb); struct nve0_ram *ram = (void *)pfb->ram; - u8 strap, cnt, len; - - /* lookup memory config data relevant to the target frequency */ - ram->base.rammap.data = nvbios_rammapEp(bios, freq / 1000, - &ram->base.rammap.version, - &ram->base.rammap.size, - &cnt, &len, &data->bios); - if (!ram->base.rammap.data || ram->base.rammap.version != 0x11 || - ram->base.rammap.size < 0x09) { - nv_error(pfb, "invalid/missing rammap entry\n"); - return -EINVAL; - } - - /* locate specific data set for the attached memory */ - strap = nvbios_ramcfg_index(nv_subdev(pfb)); - ram->base.ramcfg.data = nvbios_rammapSp(bios, ram->base.rammap.data, - ram->base.rammap.version, - ram->base.rammap.size, - cnt, len, strap, - &ram->base.ramcfg.version, - &ram->base.ramcfg.size, - &data->bios); - if (!ram->base.ramcfg.data || ram->base.ramcfg.version != 0x11 || - ram->base.ramcfg.size < 0x08) { - nv_error(pfb, "invalid/missing ramcfg entry\n"); - return -EINVAL; - } - - /* lookup memory timings, if bios says they're present */ - strap = nv_ro08(bios, ram->base.ramcfg.data + 0x00); - if (strap != 0xff) { - ram->base.timing.data = - nvbios_timingEp(bios, strap, &ram->base.timing.version, - &ram->base.timing.size, &cnt, &len, - &data->bios); - if (!ram->base.timing.data || - ram->base.timing.version != 0x20 || - ram->base.timing.size < 0x33) { - nv_error(pfb, "invalid/missing timing entry\n"); - return -EINVAL; + struct nouveau_ram_data *cfg; + u32 mhz = khz / 1000; + + list_for_each_entry(cfg, &ram->cfg, head) { + if (mhz >= cfg->bios.rammap_min && + mhz <= cfg->bios.rammap_max) { + *data = *cfg; + data->freq = khz; + return 0; } - } else { - ram->base.timing.data = 0; } - data->freq = freq; - return 0; + nv_error(ram, "ramcfg data for %dMHz not found\n", mhz); + return -EINVAL; } static int @@ -1106,13 +1072,99 @@ nve0_ram_calc(struct nouveau_fb *pfb, u32 freq) return nve0_ram_calc_xits(pfb, ram->base.next); } +static void +nve0_ram_prog_0(struct nouveau_fb *pfb, u32 freq) +{ + struct nve0_ram *ram = (void *)pfb->ram; + struct nouveau_ram_data *cfg; + u32 mhz = freq / 1000; + u32 mask, data; + + list_for_each_entry(cfg, &ram->cfg, head) { + if (mhz >= cfg->bios.rammap_min && + mhz <= cfg->bios.rammap_max) + break; + } + + if (&cfg->head == &ram->cfg) + return; + + if (mask = 0, data = 0, ram->diff.rammap_11_0a_03fe) { + data |= cfg->bios.rammap_11_0a_03fe << 12; + mask |= 0x001ff000; + } + if (ram->diff.rammap_11_09_01ff) { + data |= cfg->bios.rammap_11_09_01ff; + mask |= 0x000001ff; + } + nv_mask(pfb, 0x10f468, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0a_0400) { + data |= cfg->bios.rammap_11_0a_0400; + mask |= 0x00000001; + } + nv_mask(pfb, 0x10f420, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0a_0800) { + data |= cfg->bios.rammap_11_0a_0800; + mask |= 0x00000001; + } + nv_mask(pfb, 0x10f430, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0b_01f0) { + data |= cfg->bios.rammap_11_0b_01f0; + mask |= 0x0000001f; + } + nv_mask(pfb, 0x10f400, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0b_0200) { + data |= cfg->bios.rammap_11_0b_0200 << 9; + mask |= 0x00000200; + } + nv_mask(pfb, 0x10f410, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0d) { + data |= cfg->bios.rammap_11_0d << 16; + mask |= 0x00ff0000; + } + if (ram->diff.rammap_11_0f) { + data |= cfg->bios.rammap_11_0f << 8; + mask |= 0x0000ff00; + } + nv_mask(pfb, 0x10f440, mask, data); + + if (mask = 0, data = 0, ram->diff.rammap_11_0e) { + data |= cfg->bios.rammap_11_0e << 8; + mask |= 0x0000ff00; + } + if (ram->diff.rammap_11_0b_0800) { + data |= cfg->bios.rammap_11_0b_0800 << 7; + mask |= 0x00000080; + } + if (ram->diff.rammap_11_0b_0400) { + data |= cfg->bios.rammap_11_0b_0400 << 5; + mask |= 0x00000020; + } + nv_mask(pfb, 0x10f444, mask, data); +} + static int nve0_ram_prog(struct nouveau_fb *pfb) { struct nouveau_device *device = nv_device(pfb); struct nve0_ram *ram = (void *)pfb->ram; struct nve0_ramfuc *fuc = &ram->fuc; - ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", true)); + struct nouveau_ram_data *next = ram->base.next; + + if (!nouveau_boolopt(device->cfgopt, "NvMemExec", true)) { + ram_exec(fuc, false); + return (ram->base.next == &ram->base.xition); + } + + nve0_ram_prog_0(pfb, 1000); + ram_exec(fuc, true); + nve0_ram_prog_0(pfb, next->freq); + return (ram->base.next == &ram->base.xition); } @@ -1125,24 +1177,147 @@ nve0_ram_tidy(struct nouveau_fb *pfb) ram_exec(fuc, false); } +struct nve0_ram_train { + u16 mask; + struct nvbios_M0209S remap; + struct nvbios_M0209S type00; + struct nvbios_M0209S type01; + struct nvbios_M0209S type04; + struct nvbios_M0209S type06; + struct nvbios_M0209S type07; + struct nvbios_M0209S type08; + struct nvbios_M0209S type09; +}; + +static int +nve0_ram_train_type(struct nouveau_fb *pfb, int i, u8 ramcfg, + struct nve0_ram_train *train) +{ + struct nouveau_bios *bios = nouveau_bios(pfb); + struct nvbios_M0205E M0205E; + struct nvbios_M0205S M0205S; + struct nvbios_M0209E M0209E; + struct nvbios_M0209S *remap = &train->remap; + struct nvbios_M0209S *value; + u8 ver, hdr, cnt, len; + u32 data; + + /* determine type of data for this index */ + if (!(data = nvbios_M0205Ep(bios, i, &ver, &hdr, &cnt, &len, &M0205E))) + return -ENOENT; + + switch (M0205E.type) { + case 0x00: value = &train->type00; break; + case 0x01: value = &train->type01; break; + case 0x04: value = &train->type04; break; + case 0x06: value = &train->type06; break; + case 0x07: value = &train->type07; break; + case 0x08: value = &train->type08; break; + case 0x09: value = &train->type09; break; + default: + return 0; + } + + /* training data index determined by ramcfg strap */ + if (!(data = nvbios_M0205Sp(bios, i, ramcfg, &ver, &hdr, &M0205S))) + return -EINVAL; + i = M0205S.data; + + /* training data format information */ + if (!(data = nvbios_M0209Ep(bios, i, &ver, &hdr, &cnt, &len, &M0209E))) + return -EINVAL; + + /* ... and the raw data */ + if (!(data = nvbios_M0209Sp(bios, i, 0, &ver, &hdr, value))) + return -EINVAL; + + if (M0209E.v02_07 == 2) { + /* of course! why wouldn't we have a pointer to another entry + * in the same table, and use the first one as an array of + * remap indices... + */ + if (!(data = nvbios_M0209Sp(bios, M0209E.v03, 0, &ver, &hdr, + remap))) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(value->data); i++) + value->data[i] = remap->data[value->data[i]]; + } else + if (M0209E.v02_07 != 1) + return -EINVAL; + + train->mask |= 1 << M0205E.type; + return 0; +} + +static int +nve0_ram_train_init_0(struct nouveau_fb *pfb, struct nve0_ram_train *train) +{ + int i, j; + + if ((train->mask & 0x03d3) != 0x03d3) { + nv_warn(pfb, "missing link training data\n"); + return -EINVAL; + } + + for (i = 0; i < 0x30; i++) { + for (j = 0; j < 8; j += 4) { + nv_wr32(pfb, 0x10f968 + j, 0x00000000 | (i << 8)); + nv_wr32(pfb, 0x10f920 + j, 0x00000000 | + train->type08.data[i] << 4 | + train->type06.data[i]); + nv_wr32(pfb, 0x10f918 + j, train->type00.data[i]); + nv_wr32(pfb, 0x10f920 + j, 0x00000100 | + train->type09.data[i] << 4 | + train->type07.data[i]); + nv_wr32(pfb, 0x10f918 + j, train->type01.data[i]); + } + } + + for (j = 0; j < 8; j += 4) { + for (i = 0; i < 0x100; i++) { + nv_wr32(pfb, 0x10f968 + j, i); + nv_wr32(pfb, 0x10f900 + j, train->type04.data[i]); + } + } + + return 0; +} + +static int +nve0_ram_train_init(struct nouveau_fb *pfb) +{ + u8 ramcfg = nvbios_ramcfg_index(nv_subdev(pfb)); + struct nve0_ram_train *train; + int ret = -ENOMEM, i; + + if ((train = kzalloc(sizeof(*train), GFP_KERNEL))) { + for (i = 0; i < 0x100; i++) { + ret = nve0_ram_train_type(pfb, i, ramcfg, train); + if (ret && ret != -ENOENT) + break; + } + } + + switch (pfb->ram->type) { + case NV_MEM_TYPE_GDDR5: + ret = nve0_ram_train_init_0(pfb, train); + break; + default: + ret = 0; + break; + } + + kfree(train); + return ret; +} + int nve0_ram_init(struct nouveau_object *object) { struct nouveau_fb *pfb = (void *)object->parent; struct nve0_ram *ram = (void *)object; struct nouveau_bios *bios = nouveau_bios(pfb); - static const u8 train0[] = { - 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, - 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, - }; - static const u32 train1[] = { - 0x00000000, 0xffffffff, - 0x55555555, 0xaaaaaaaa, - 0x33333333, 0xcccccccc, - 0xf0f0f0f0, 0x0f0f0f0f, - 0x00ff00ff, 0xff00ff00, - 0x0000ffff, 0xffff0000, - }; u8 ver, hdr, cnt, len, snr, ssz; u32 data, save; int ret, i; @@ -1168,51 +1343,107 @@ nve0_ram_init(struct nouveau_object *object) cnt = nv_ro08(bios, data + 0x14); /* guess at count */ data = nv_ro32(bios, data + 0x10); /* guess u32... */ - save = nv_rd32(pfb, 0x10f65c); - for (i = 0; i < cnt; i++) { - nv_mask(pfb, 0x10f65c, 0x000000f0, i << 4); - nvbios_exec(&(struct nvbios_init) { - .subdev = nv_subdev(pfb), - .bios = bios, - .offset = nv_ro32(bios, data), /* guess u32 */ - .execute = 1, - }); - data += 4; - } - nv_wr32(pfb, 0x10f65c, save); + save = nv_rd32(pfb, 0x10f65c) & 0x000000f0; + for (i = 0; i < cnt; i++, data += 4) { + if (i != save >> 4) { + nv_mask(pfb, 0x10f65c, 0x000000f0, i << 4); + nvbios_exec(&(struct nvbios_init) { + .subdev = nv_subdev(pfb), + .bios = bios, + .offset = nv_ro32(bios, data), + .execute = 1, + }); + } + } + nv_mask(pfb, 0x10f65c, 0x000000f0, save); nv_mask(pfb, 0x10f584, 0x11000000, 0x00000000); + nv_wr32(pfb, 0x10ecc0, 0xffffffff); + nv_mask(pfb, 0x10f160, 0x00000010, 0x00000010); - switch (ram->base.type) { - case NV_MEM_TYPE_GDDR5: - for (i = 0; i < 0x30; i++) { - nv_wr32(pfb, 0x10f968, 0x00000000 | (i << 8)); - nv_wr32(pfb, 0x10f920, 0x00000000 | train0[i % 12]); - nv_wr32(pfb, 0x10f918, train1[i % 12]); - nv_wr32(pfb, 0x10f920, 0x00000100 | train0[i % 12]); - nv_wr32(pfb, 0x10f918, train1[i % 12]); - - nv_wr32(pfb, 0x10f96c, 0x00000000 | (i << 8)); - nv_wr32(pfb, 0x10f924, 0x00000000 | train0[i % 12]); - nv_wr32(pfb, 0x10f91c, train1[i % 12]); - nv_wr32(pfb, 0x10f924, 0x00000100 | train0[i % 12]); - nv_wr32(pfb, 0x10f91c, train1[i % 12]); - } + return nve0_ram_train_init(pfb); +} - for (i = 0; i < 0x100; i++) { - nv_wr32(pfb, 0x10f968, i); - nv_wr32(pfb, 0x10f900, train1[2 + (i & 1)]); - } +static int +nve0_ram_ctor_data(struct nve0_ram *ram, u8 ramcfg, int i) +{ + struct nouveau_fb *pfb = (void *)nv_object(ram)->parent; + struct nouveau_bios *bios = nouveau_bios(pfb); + struct nouveau_ram_data *cfg; + struct nvbios_ramcfg *d = &ram->diff; + struct nvbios_ramcfg *p, *n; + u8 ver, hdr, cnt, len; + u32 data; + int ret; - for (i = 0; i < 0x100; i++) { - nv_wr32(pfb, 0x10f96c, i); - nv_wr32(pfb, 0x10f900, train1[2 + (i & 1)]); - } - break; - default: - break; + if (!(cfg = kmalloc(sizeof(*cfg), GFP_KERNEL))) + return -ENOMEM; + p = &list_last_entry(&ram->cfg, typeof(*cfg), head)->bios; + n = &cfg->bios; + + /* memory config data for a range of target frequencies */ + data = nvbios_rammapEp(bios, i, &ver, &hdr, &cnt, &len, &cfg->bios); + if (ret = -ENOENT, !data) + goto done; + if (ret = -ENOSYS, ver != 0x11 || hdr < 0x12) + goto done; + + /* ... and a portion specific to the attached memory */ + data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, ramcfg, + &ver, &hdr, &cfg->bios); + if (ret = -EINVAL, !data) + goto done; + if (ret = -ENOSYS, ver != 0x11 || hdr < 0x0a) + goto done; + + /* lookup memory timings, if bios says they're present */ + if (cfg->bios.ramcfg_timing != 0xff) { + data = nvbios_timingEp(bios, cfg->bios.ramcfg_timing, + &ver, &hdr, &cnt, &len, + &cfg->bios); + if (ret = -EINVAL, !data) + goto done; + if (ret = -ENOSYS, ver != 0x20 || hdr < 0x33) + goto done; } - return 0; + list_add_tail(&cfg->head, &ram->cfg); + if (ret = 0, i == 0) + goto done; + + d->rammap_11_0a_03fe |= p->rammap_11_0a_03fe != n->rammap_11_0a_03fe; + d->rammap_11_09_01ff |= p->rammap_11_09_01ff != n->rammap_11_09_01ff; + d->rammap_11_0a_0400 |= p->rammap_11_0a_0400 != n->rammap_11_0a_0400; + d->rammap_11_0a_0800 |= p->rammap_11_0a_0800 != n->rammap_11_0a_0800; + d->rammap_11_0b_01f0 |= p->rammap_11_0b_01f0 != n->rammap_11_0b_01f0; + d->rammap_11_0b_0200 |= p->rammap_11_0b_0200 != n->rammap_11_0b_0200; + d->rammap_11_0d |= p->rammap_11_0d != n->rammap_11_0d; + d->rammap_11_0f |= p->rammap_11_0f != n->rammap_11_0f; + d->rammap_11_0e |= p->rammap_11_0e != n->rammap_11_0e; + d->rammap_11_0b_0800 |= p->rammap_11_0b_0800 != n->rammap_11_0b_0800; + d->rammap_11_0b_0400 |= p->rammap_11_0b_0400 != n->rammap_11_0b_0400; + d->ramcfg_11_01_01 |= p->ramcfg_11_01_01 != n->ramcfg_11_01_01; + d->ramcfg_11_01_02 |= p->ramcfg_11_01_02 != n->ramcfg_11_01_02; + d->ramcfg_11_01_10 |= p->ramcfg_11_01_10 != n->ramcfg_11_01_10; + d->ramcfg_11_02_03 |= p->ramcfg_11_02_03 != n->ramcfg_11_02_03; + d->ramcfg_11_08_20 |= p->ramcfg_11_08_20 != n->ramcfg_11_08_20; + d->timing_20_30_07 |= p->timing_20_30_07 != n->timing_20_30_07; +done: + if (ret) + kfree(cfg); + return ret; +} + +static void +nve0_ram_dtor(struct nouveau_object *object) +{ + struct nve0_ram *ram = (void *)object; + struct nouveau_ram_data *cfg, *tmp; + + list_for_each_entry_safe(cfg, tmp, &ram->cfg, head) { + kfree(cfg); + } + + nouveau_ram_destroy(&ram->base); } static int @@ -1226,6 +1457,7 @@ nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct dcb_gpio_func func; struct nve0_ram *ram; int ret, i; + u8 ramcfg = nvbios_ramcfg_index(nv_subdev(pfb)); u32 tmp; ret = nvc0_ram_create(parent, engine, oclass, 0x022554, &ram); @@ -1233,6 +1465,8 @@ nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + INIT_LIST_HEAD(&ram->cfg); + switch (ram->base.type) { case NV_MEM_TYPE_DDR3: case NV_MEM_TYPE_GDDR5: @@ -1264,7 +1498,26 @@ nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine, } } - // parse bios data for both pll's + /* parse bios data for all rammap table entries up-front, and + * build information on whether certain fields differ between + * any of the entries. + * + * the binary driver appears to completely ignore some fields + * when all entries contain the same value. at first, it was + * hoped that these were mere optimisations and the bios init + * tables had configured as per the values here, but there is + * evidence now to suggest that this isn't the case and we do + * need to treat this condition as a "don't touch" indicator. + */ + for (i = 0; !ret; i++) { + ret = nve0_ram_ctor_data(ram, ramcfg, i); + if (ret && ret != -ENOENT) { + nv_error(pfb, "failed to parse ramcfg data\n"); + return ret; + } + } + + /* parse bios data for both pll's */ ret = nvbios_pll_parse(bios, 0x0c, &ram->fuc.refpll); if (ret) { nv_error(pfb, "mclk refpll data not found\n"); @@ -1277,6 +1530,7 @@ nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine, return ret; } + /* lookup memory voltage gpios */ ret = gpio->find(gpio, 0, 0x18, DCB_GPIO_UNUSED, &func); if (ret == 0) { ram->fuc.r_gpioMV = ramfuc_reg(0x00d610 + (func.line * 0x04)); @@ -1385,7 +1639,7 @@ nve0_ram_oclass = { .handle = 0, .ofuncs = &(struct nouveau_ofuncs) { .ctor = nve0_ram_ctor, - .dtor = _nouveau_ram_dtor, + .dtor = nve0_ram_dtor, .init = nve0_ram_init, .fini = _nouveau_ram_fini, } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/sddr2.c b/drivers/gpu/drm/nouveau/core/subdev/fb/sddr2.c new file mode 100644 index 0000000000000000000000000000000000000000..bb1eb8f3e63918bdbcc82c5eb0b97af3370b7879 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/sddr2.c @@ -0,0 +1,94 @@ +/* + * Copyright 2014 Roy Spliet + * + * 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. + * + * Authors: Roy Spliet + * Ben Skeggs + */ + +#include "priv.h" + +struct ramxlat { + int id; + u8 enc; +}; + +static inline int +ramxlat(const struct ramxlat *xlat, int id) +{ + while (xlat->id >= 0) { + if (xlat->id == id) + return xlat->enc; + xlat++; + } + return -EINVAL; +} + +static const struct ramxlat +ramddr2_cl[] = { + { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, + /* The following are available in some, but not all DDR2 docs */ + { 7, 7 }, + { -1 } +}; + +static const struct ramxlat +ramddr2_wr[] = { + { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 4 }, { 6, 5 }, + /* The following are available in some, but not all DDR2 docs */ + { 7, 6 }, + { -1 } +}; + +int +nouveau_sddr2_calc(struct nouveau_ram *ram) +{ + int CL, WR, DLL = 0, ODT = 0; + + switch (ram->next->bios.timing_ver) { + case 0x10: + CL = ram->next->bios.timing_10_CL; + WR = ram->next->bios.timing_10_WR; + DLL = !ram->next->bios.ramcfg_10_02_40; + ODT = ram->next->bios.timing_10_ODT & 3; + break; + case 0x20: + CL = (ram->next->bios.timing[1] & 0x0000001f); + WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16; + break; + default: + return -ENOSYS; + } + + CL = ramxlat(ramddr2_cl, CL); + WR = ramxlat(ramddr2_wr, WR); + if (CL < 0 || WR < 0) + return -EINVAL; + + ram->mr[0] &= ~0xf70; + ram->mr[0] |= (WR & 0x07) << 9; + ram->mr[0] |= (CL & 0x07) << 4; + + ram->mr[1] &= ~0x045; + ram->mr[1] |= (ODT & 0x1) << 2; + ram->mr[1] |= (ODT & 0x2) << 5; + ram->mr[1] |= !DLL; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/sddr3.c b/drivers/gpu/drm/nouveau/core/subdev/fb/sddr3.c index ebd4cd9c35d905eefc1adf51295fa0dfbafa5f1f..83949b11833af46ca1537904691ca7d13112133a 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/sddr3.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/sddr3.c @@ -20,9 +20,9 @@ * OTHER DEALINGS IN THE SOFTWARE. * * Authors: Ben Skeggs + * Roy Spliet */ -#include #include "priv.h" struct ramxlat { @@ -69,31 +69,52 @@ ramddr3_cwl[] = { int nouveau_sddr3_calc(struct nouveau_ram *ram) { - struct nouveau_bios *bios = nouveau_bios(ram); - int WL, CL, WR; + int CWL, CL, WR, DLL = 0, ODT = 0; - switch (!!ram->timing.data * ram->timing.version) { + switch (ram->next->bios.timing_ver) { + case 0x10: + if (ram->next->bios.timing_hdr < 0x17) { + /* XXX: NV50: Get CWL from the timing register */ + return -ENOSYS; + } + CWL = ram->next->bios.timing_10_CWL; + CL = ram->next->bios.timing_10_CL; + WR = ram->next->bios.timing_10_WR; + DLL = !ram->next->bios.ramcfg_10_02_40; + ODT = ram->next->bios.timing_10_ODT; + break; case 0x20: - WL = (nv_ro16(bios, ram->timing.data + 0x04) & 0x0f80) >> 7; - CL = nv_ro08(bios, ram->timing.data + 0x04) & 0x1f; - WR = nv_ro08(bios, ram->timing.data + 0x0a) & 0x7f; + CWL = (ram->next->bios.timing[1] & 0x00000f80) >> 7; + CL = (ram->next->bios.timing[1] & 0x0000001f) >> 0; + WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16; + /* XXX: Get these values from the VBIOS instead */ + DLL = !(ram->mr[1] & 0x1); + ODT = (ram->mr[1] & 0x004) >> 2 | + (ram->mr[1] & 0x040) >> 5 | + (ram->mr[1] & 0x200) >> 7; break; default: return -ENOSYS; } - WL = ramxlat(ramddr3_cwl, WL); - CL = ramxlat(ramddr3_cl, CL); - WR = ramxlat(ramddr3_wr, WR); - if (WL < 0 || CL < 0 || WR < 0) + CWL = ramxlat(ramddr3_cwl, CWL); + CL = ramxlat(ramddr3_cl, CL); + WR = ramxlat(ramddr3_wr, WR); + if (CL < 0 || CWL < 0 || WR < 0) return -EINVAL; - ram->mr[0] &= ~0xe74; + ram->mr[0] &= ~0xf74; ram->mr[0] |= (WR & 0x07) << 9; ram->mr[0] |= (CL & 0x0e) << 3; ram->mr[0] |= (CL & 0x01) << 2; + ram->mr[1] &= ~0x245; + ram->mr[1] |= (ODT & 0x1) << 2; + ram->mr[1] |= (ODT & 0x2) << 5; + ram->mr[1] |= (ODT & 0x4) << 7; + ram->mr[1] |= !DLL; + ram->mr[2] &= ~0x038; - ram->mr[2] |= (WL & 0x07) << 3; + ram->mr[2] |= (CWL & 0x07) << 3; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fuse/base.c b/drivers/gpu/drm/nouveau/core/subdev/fuse/base.c new file mode 100644 index 0000000000000000000000000000000000000000..9e8e92127715016b7acefc4fbdb81a5f8a09b027 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fuse/base.c @@ -0,0 +1,54 @@ +/* + * Copyright 2014 Martin Peres + * + * 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. + * + * Authors: Martin Peres + */ + +#include + +int +_nouveau_fuse_init(struct nouveau_object *object) +{ + struct nouveau_fuse *fuse = (void *)object; + return nouveau_subdev_init(&fuse->base); +} + +void +_nouveau_fuse_dtor(struct nouveau_object *object) +{ + struct nouveau_fuse *fuse = (void *)object; + nouveau_subdev_destroy(&fuse->base); +} + +int +nouveau_fuse_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, int length, void **pobject) +{ + struct nouveau_fuse *fuse; + int ret; + + ret = nouveau_subdev_create_(parent, engine, oclass, 0, "FUSE", + "fuse", length, pobject); + fuse = *pobject; + + return ret; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/fuse/g80.c b/drivers/gpu/drm/nouveau/core/subdev/fuse/g80.c new file mode 100644 index 0000000000000000000000000000000000000000..a374ade485bed8196e575669df8910d650821076 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fuse/g80.c @@ -0,0 +1,81 @@ +/* + * Copyright 2014 Martin Peres + * + * 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. + * + * Authors: Martin Peres + */ + +#include "priv.h" + +struct g80_fuse_priv { + struct nouveau_fuse base; + + spinlock_t fuse_enable_lock; +}; + +static u32 +g80_fuse_rd32(struct nouveau_object *object, u64 addr) +{ + struct g80_fuse_priv *priv = (void *)object; + unsigned long flags; + u32 fuse_enable, val; + + spin_lock_irqsave(&priv->fuse_enable_lock, flags); + + /* racy if another part of nouveau start writing to this reg */ + fuse_enable = nv_mask(priv, 0x1084, 0x800, 0x800); + val = nv_rd32(priv, 0x21000 + addr); + nv_wr32(priv, 0x1084, fuse_enable); + + spin_unlock_irqrestore(&priv->fuse_enable_lock, flags); + + return val; +} + + +static int +g80_fuse_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct g80_fuse_priv *priv; + int ret; + + ret = nouveau_fuse_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + spin_lock_init(&priv->fuse_enable_lock); + + return 0; +} + +struct nouveau_oclass +g80_fuse_oclass = { + .handle = NV_SUBDEV(FUSE, 0x50), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = g80_fuse_ctor, + .dtor = _nouveau_fuse_dtor, + .init = _nouveau_fuse_init, + .fini = _nouveau_fuse_fini, + .rd32 = g80_fuse_rd32, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fuse/gf100.c b/drivers/gpu/drm/nouveau/core/subdev/fuse/gf100.c new file mode 100644 index 0000000000000000000000000000000000000000..5ed03f54b3d4eca4ad323f0edd9742d0258d3059 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fuse/gf100.c @@ -0,0 +1,83 @@ +/* + * Copyright 2014 Martin Peres + * + * 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. + * + * Authors: Martin Peres + */ + +#include "priv.h" + +struct gf100_fuse_priv { + struct nouveau_fuse base; + + spinlock_t fuse_enable_lock; +}; + +static u32 +gf100_fuse_rd32(struct nouveau_object *object, u64 addr) +{ + struct gf100_fuse_priv *priv = (void *)object; + unsigned long flags; + u32 fuse_enable, unk, val; + + spin_lock_irqsave(&priv->fuse_enable_lock, flags); + + /* racy if another part of nouveau start writing to these regs */ + fuse_enable = nv_mask(priv, 0x22400, 0x800, 0x800); + unk = nv_mask(priv, 0x21000, 0x1, 0x1); + val = nv_rd32(priv, 0x21100 + addr); + nv_wr32(priv, 0x21000, unk); + nv_wr32(priv, 0x22400, fuse_enable); + + spin_unlock_irqrestore(&priv->fuse_enable_lock, flags); + + return val; +} + + +static int +gf100_fuse_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct gf100_fuse_priv *priv; + int ret; + + ret = nouveau_fuse_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + spin_lock_init(&priv->fuse_enable_lock); + + return 0; +} + +struct nouveau_oclass +gf100_fuse_oclass = { + .handle = NV_SUBDEV(FUSE, 0xC0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = gf100_fuse_ctor, + .dtor = _nouveau_fuse_dtor, + .init = _nouveau_fuse_init, + .fini = _nouveau_fuse_fini, + .rd32 = gf100_fuse_rd32, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fuse/gm107.c b/drivers/gpu/drm/nouveau/core/subdev/fuse/gm107.c new file mode 100644 index 0000000000000000000000000000000000000000..4f1a636c6538dd27fadc6652bac81b0da9cde692 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fuse/gm107.c @@ -0,0 +1,66 @@ +/* + * Copyright 2014 Martin Peres + * + * 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. + * + * Authors: Martin Peres + */ + +#include "priv.h" + +struct gm107_fuse_priv { + struct nouveau_fuse base; +}; + +static u32 +gm107_fuse_rd32(struct nouveau_object *object, u64 addr) +{ + struct gf100_fuse_priv *priv = (void *)object; + + return nv_rd32(priv, 0x21100 + addr); +} + + +static int +gm107_fuse_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct gm107_fuse_priv *priv; + int ret; + + ret = nouveau_fuse_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + return 0; +} + +struct nouveau_oclass +gm107_fuse_oclass = { + .handle = NV_SUBDEV(FUSE, 0x117), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = gm107_fuse_ctor, + .dtor = _nouveau_fuse_dtor, + .init = _nouveau_fuse_init, + .fini = _nouveau_fuse_fini, + .rd32 = gm107_fuse_rd32, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fuse/priv.h b/drivers/gpu/drm/nouveau/core/subdev/fuse/priv.h new file mode 100644 index 0000000000000000000000000000000000000000..d2085411a5cba55ee2f2828ee578ec77f88c51d0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fuse/priv.h @@ -0,0 +1,9 @@ +#ifndef __NVKM_FUSE_PRIV_H__ +#define __NVKM_FUSE_PRIV_H__ + +#include + +int _nouveau_fuse_init(struct nouveau_object *object); +void _nouveau_fuse_dtor(struct nouveau_object *object); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c index b1e3ed7c8beb51a555b78be072a79faf39e369c7..7ad99b763f4c463334f7642a910e9f79fe8a9777 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c @@ -122,7 +122,8 @@ nouveau_gpio_intr_init(struct nvkm_event *event, int type, int index) } static int -nouveau_gpio_intr_ctor(void *data, u32 size, struct nvkm_notify *notify) +nouveau_gpio_intr_ctor(struct nouveau_object *object, void *data, u32 size, + struct nvkm_notify *notify) { struct nvkm_gpio_ntfy_req *req = data; if (!WARN_ON(size != sizeof(*req))) { diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv94.c similarity index 88% rename from drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c rename to drivers/gpu/drm/nouveau/core/subdev/gpio/nv94.c index 252083d376f512bcc4c4278207181d8c57f226f4..cae404ccadac7eeb313f6c3b8dbc82cb17124d29 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv94.c @@ -25,7 +25,7 @@ #include "priv.h" void -nv92_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo) +nv94_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo) { u32 intr0 = nv_rd32(gpio, 0x00e054); u32 intr1 = nv_rd32(gpio, 0x00e074); @@ -38,7 +38,7 @@ nv92_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo) } void -nv92_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data) +nv94_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data) { u32 inte0 = nv_rd32(gpio, 0x00e050); u32 inte1 = nv_rd32(gpio, 0x00e070); @@ -57,8 +57,8 @@ nv92_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data) } struct nouveau_oclass * -nv92_gpio_oclass = &(struct nouveau_gpio_impl) { - .base.handle = NV_SUBDEV(GPIO, 0x92), +nv94_gpio_oclass = &(struct nouveau_gpio_impl) { + .base.handle = NV_SUBDEV(GPIO, 0x94), .base.ofuncs = &(struct nouveau_ofuncs) { .ctor = _nouveau_gpio_ctor, .dtor = _nouveau_gpio_dtor, @@ -66,8 +66,8 @@ nv92_gpio_oclass = &(struct nouveau_gpio_impl) { .fini = _nouveau_gpio_fini, }, .lines = 32, - .intr_stat = nv92_gpio_intr_stat, - .intr_mask = nv92_gpio_intr_mask, + .intr_stat = nv94_gpio_intr_stat, + .intr_mask = nv94_gpio_intr_mask, .drive = nv50_gpio_drive, .sense = nv50_gpio_sense, .reset = nv50_gpio_reset, diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c index a4682b0956ad3ef325451eb083393c4a741e8cee..480d6d2af770c98dde41c97bce82b5fd124cf2c2 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c @@ -77,8 +77,8 @@ nvd0_gpio_oclass = &(struct nouveau_gpio_impl) { .fini = _nouveau_gpio_fini, }, .lines = 32, - .intr_stat = nv92_gpio_intr_stat, - .intr_mask = nv92_gpio_intr_mask, + .intr_stat = nv94_gpio_intr_stat, + .intr_mask = nv94_gpio_intr_mask, .drive = nvd0_gpio_drive, .sense = nvd0_gpio_sense, .reset = nvd0_gpio_reset, diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h b/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h index e1724dfc86ae2edfe9801e59acd749203f304977..bff98b86e2b53b5cce1f7439cd7f0a48be667032 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h +++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h @@ -56,8 +56,8 @@ void nv50_gpio_reset(struct nouveau_gpio *, u8); int nv50_gpio_drive(struct nouveau_gpio *, int, int, int); int nv50_gpio_sense(struct nouveau_gpio *, int); -void nv92_gpio_intr_stat(struct nouveau_gpio *, u32 *, u32 *); -void nv92_gpio_intr_mask(struct nouveau_gpio *, u32, u32, u32); +void nv94_gpio_intr_stat(struct nouveau_gpio *, u32 *, u32 *); +void nv94_gpio_intr_mask(struct nouveau_gpio *, u32, u32, u32); void nvd0_gpio_reset(struct nouveau_gpio *, u8); int nvd0_gpio_drive(struct nouveau_gpio *, int, int, int); diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c index a652cafde3d6d01ddc4d5173927a3c6a5d6c78d2..2b1bf545e488df2b6b6ff99253e2cb38895de48f 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c @@ -23,6 +23,7 @@ */ #include +#include #include #include @@ -346,7 +347,8 @@ nouveau_i2c_intr_init(struct nvkm_event *event, int type, int index) } static int -nouveau_i2c_intr_ctor(void *data, u32 size, struct nvkm_notify *notify) +nouveau_i2c_intr_ctor(struct nouveau_object *object, void *data, u32 size, + struct nvkm_notify *notify) { struct nvkm_i2c_ntfy_req *req = data; if (!WARN_ON(size != sizeof(*req))) { diff --git a/drivers/gpu/drm/nouveau/core/subdev/instmem/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/instmem/nv04.c index 7b64befee48fbb5b30b306c980ae53bd077c3368..e8b1401c59c0ef4c76fa9407eab862a8b8360dc3 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/instmem/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/instmem/nv04.c @@ -69,7 +69,7 @@ nv04_instobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - ret = nouveau_mm_head(&priv->heap, 1, args->size, args->size, + ret = nouveau_mm_head(&priv->heap, 0, 1, args->size, args->size, args->align, &node->mem); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/subdev/ltc/base.c b/drivers/gpu/drm/nouveau/core/subdev/ltc/base.c index 32ed442c59130548f0deeb88fb735048a7e45a9a..7fa331516f84f22bf27b9795e8e181b1b899335a 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/ltc/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/ltc/base.c @@ -31,7 +31,7 @@ nvkm_ltc_tags_alloc(struct nouveau_ltc *ltc, u32 n, struct nvkm_ltc_priv *priv = (void *)ltc; int ret; - ret = nouveau_mm_head(&priv->tags, 1, n, n, 1, pnode); + ret = nouveau_mm_head(&priv->tags, 0, 1, n, n, 1, pnode); if (ret) *pnode = NULL; diff --git a/drivers/gpu/drm/nouveau/core/subdev/ltc/gf100.c b/drivers/gpu/drm/nouveau/core/subdev/ltc/gf100.c index d5d65285efe59188072f90fc020939d14c3f1dec..2db0977284f80dfb484f34969809491daf9cbbf7 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/ltc/gf100.c +++ b/drivers/gpu/drm/nouveau/core/subdev/ltc/gf100.c @@ -62,16 +62,38 @@ gf100_ltc_zbc_clear_depth(struct nvkm_ltc_priv *priv, int i, const u32 depth) nv_wr32(priv, 0x17ea58, depth); } +static const struct nouveau_bitfield +gf100_ltc_lts_intr_name[] = { + { 0x00000001, "IDLE_ERROR_IQ" }, + { 0x00000002, "IDLE_ERROR_CBC" }, + { 0x00000004, "IDLE_ERROR_TSTG" }, + { 0x00000008, "IDLE_ERROR_DSTG" }, + { 0x00000010, "EVICTED_CB" }, + { 0x00000020, "ILLEGAL_COMPSTAT" }, + { 0x00000040, "BLOCKLINEAR_CB" }, + { 0x00000100, "ECC_SEC_ERROR" }, + { 0x00000200, "ECC_DED_ERROR" }, + { 0x00000400, "DEBUG" }, + { 0x00000800, "ATOMIC_TO_Z" }, + { 0x00001000, "ILLEGAL_ATOMIC" }, + { 0x00002000, "BLKACTIVITY_ERR" }, + {} +}; + static void -gf100_ltc_lts_isr(struct nvkm_ltc_priv *priv, int ltc, int lts) +gf100_ltc_lts_intr(struct nvkm_ltc_priv *priv, int ltc, int lts) { u32 base = 0x141000 + (ltc * 0x2000) + (lts * 0x400); - u32 stat = nv_rd32(priv, base + 0x020); + u32 intr = nv_rd32(priv, base + 0x020); + u32 stat = intr & 0x0000ffff; if (stat) { - nv_info(priv, "LTC%d_LTS%d: 0x%08x\n", ltc, lts, stat); - nv_wr32(priv, base + 0x020, stat); + nv_info(priv, "LTC%d_LTS%d:", ltc, lts); + nouveau_bitfield_print(gf100_ltc_lts_intr_name, stat); + pr_cont("\n"); } + + nv_wr32(priv, base + 0x020, intr); } void @@ -84,14 +106,9 @@ gf100_ltc_intr(struct nouveau_subdev *subdev) while (mask) { u32 lts, ltc = __ffs(mask); for (lts = 0; lts < priv->lts_nr; lts++) - gf100_ltc_lts_isr(priv, ltc, lts); + gf100_ltc_lts_intr(priv, ltc, lts); mask &= ~(1 << ltc); } - - /* we do something horribly wrong and upset PMFB a lot, so mask off - * interrupts from it after the first one until it's fixed - */ - nv_mask(priv, 0x000640, 0x02000000, 0x00000000); } static int @@ -153,7 +170,7 @@ gf100_ltc_init_tag_ram(struct nouveau_fb *pfb, struct nvkm_ltc_priv *priv) tag_size += tag_align; tag_size = (tag_size + 0xfff) >> 12; /* round up */ - ret = nouveau_mm_tail(&pfb->vram, 1, tag_size, tag_size, 1, + ret = nouveau_mm_tail(&pfb->vram, 1, 1, tag_size, tag_size, 1, &priv->tag_ram); if (ret) { priv->num_tags = 0; diff --git a/drivers/gpu/drm/nouveau/core/subdev/ltc/gm107.c b/drivers/gpu/drm/nouveau/core/subdev/ltc/gm107.c index a4de64289762fc9cfe82ab40e424b4fba67fe0f0..89fc4238f50c4afea16fe2d5cb3501f5eb954216 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/ltc/gm107.c +++ b/drivers/gpu/drm/nouveau/core/subdev/ltc/gm107.c @@ -87,11 +87,6 @@ gm107_ltc_intr(struct nouveau_subdev *subdev) gm107_ltc_lts_isr(priv, ltc, lts); mask &= ~(1 << ltc); } - - /* we do something horribly wrong and upset PMFB a lot, so mask off - * interrupts from it after the first one until it's fixed - */ - nv_mask(priv, 0x000640, 0x02000000, 0x00000000); } static int diff --git a/drivers/gpu/drm/nouveau/core/subdev/ltc/priv.h b/drivers/gpu/drm/nouveau/core/subdev/ltc/priv.h index 594924f391261b9db9e25eba5940a908252e97e4..41f179d93da697e4ca6ea4fd41fef7077779dd25 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/ltc/priv.h +++ b/drivers/gpu/drm/nouveau/core/subdev/ltc/priv.h @@ -4,6 +4,8 @@ #include #include +#include + struct nvkm_ltc_priv { struct nouveau_ltc base; u32 ltc_nr; diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/base.c b/drivers/gpu/drm/nouveau/core/subdev/pwr/base.c index 69f1f34f6931d56f53cad1c16faf4d5caef7a935..0ab55f27ec450663ac47adb53e449f1e62215a67 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/base.c @@ -203,6 +203,8 @@ _nouveau_pwr_init(struct nouveau_object *object) nv_wait(ppwr, 0x10a04c, 0xffffffff, 0x00000000); nv_mask(ppwr, 0x000200, 0x00002000, 0x00000000); nv_mask(ppwr, 0x000200, 0x00002000, 0x00002000); + nv_rd32(ppwr, 0x000200); + nv_wait(ppwr, 0x10a10c, 0x00000006, 0x00000000); /* upload data segment */ nv_wr32(ppwr, 0x10a1c0, 0x01000000); diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/arith.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/arith.fuc new file mode 100644 index 0000000000000000000000000000000000000000..214a6d9e088d90d658d90826e5f50596b4f39b94 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/arith.fuc @@ -0,0 +1,94 @@ +/* + * Copyright 2014 Martin Peres + * + * 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 folloing 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. + * + * Authors: Martin Peres + */ + +/****************************************************************************** + * arith data segment + *****************************************************************************/ +#ifdef INCLUDE_PROC +#endif + +#ifdef INCLUDE_DATA +#endif + +/****************************************************************************** + * arith code segment + *****************************************************************************/ +#ifdef INCLUDE_CODE + +// does a 32x32 -> 64 multiplication +// +// A * B = A_lo * B_lo +// + ( A_hi * B_lo ) << 16 +// + ( A_lo * B_hi ) << 16 +// + ( A_hi * B_hi ) << 32 +// +// $r15 - current +// $r14 - A +// $r13 - B +// $r12 - mul_lo (return) +// $r11 - mul_hi (return) +// $r0 - zero +mulu32_32_64: + push $r1 // A_hi + push $r2 // B_hi + push $r3 // tmp0 + push $r4 // tmp1 + + shr b32 $r1 $r14 16 + shr b32 $r2 $r13 16 + + clear b32 $r12 + clear b32 $r11 + + // A_lo * B_lo + mulu $r12 $r14 $r13 + + // ( A_hi * B_lo ) << 16 + mulu $r3 $r1 $r13 // tmp0 = A_hi * B_lo + mov b32 $r4 $r3 + and $r3 0xffff // tmp0 = tmp0_lo + shl b32 $r3 16 + shr b32 $r4 16 // tmp1 = tmp0_hi + add b32 $r12 $r3 + adc b32 $r11 $r4 + + // ( A_lo * B_hi ) << 16 + mulu $r3 $r14 $r2 // tmp0 = A_lo * B_hi + mov b32 $r4 $r3 + and $r3 0xffff // tmp0 = tmp0_lo + shl b32 $r3 16 + shr b32 $r4 16 // tmp1 = tmp0_hi + add b32 $r12 $r3 + adc b32 $r11 $r4 + + // ( A_hi * B_hi ) << 32 + mulu $r3 $r1 $r2 // tmp0 = A_hi * B_hi + add b32 $r11 $r3 + + pop $r4 + pop $r3 + pop $r2 + pop $r1 + ret +#endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/kernel.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/kernel.fuc index 8f29badd785f16f9cd37cff7adf19d03f1aafc44..5cf5be63cbeff3ea4db031c606979d32fe413c89 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/kernel.fuc +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/kernel.fuc @@ -98,12 +98,16 @@ wr32: // $r14 - ns // $r0 - zero nsec: + push $r9 + push $r8 nv_iord($r8, NV_PPWR_TIMER_LOW) nsec_loop: nv_iord($r9, NV_PPWR_TIMER_LOW) sub b32 $r9 $r8 cmp b32 $r9 $r14 bra l #nsec_loop + pop $r8 + pop $r9 ret // busy-wait for a period of time @@ -115,6 +119,8 @@ nsec: // $r11 - timeout (ns) // $r0 - zero wait: + push $r9 + push $r8 nv_iord($r8, NV_PPWR_TIMER_LOW) wait_loop: nv_rd32($r10, $r14) @@ -126,6 +132,8 @@ wait: cmp b32 $r9 $r11 bra l #wait_loop wait_done: + pop $r8 + pop $r9 ret // $r15 - current (kern) @@ -242,12 +250,89 @@ intr: bclr $flags $p0 iret -// request the current process be sent a message after a timeout expires +// calculate the number of ticks in the specified nanoseconds delay +// +// $r15 - current +// $r14 - ns +// $r14 - ticks (return) +// $r0 - zero +ticks_from_ns: + push $r12 + push $r11 + + /* try not losing precision (multiply then divide) */ + imm32($r13, HW_TICKS_PER_US) + call #mulu32_32_64 + + /* use an immeditate, it's ok because HW_TICKS_PER_US < 16 bits */ + div $r12 $r12 1000 + + /* check if there wasn't any overflow */ + cmpu b32 $r11 0 + bra e #ticks_from_ns_quit + + /* let's divide then multiply, too bad for the precision! */ + div $r14 $r14 1000 + imm32($r13, HW_TICKS_PER_US) + call #mulu32_32_64 + + /* this cannot overflow as long as HW_TICKS_PER_US < 1000 */ + +ticks_from_ns_quit: + mov b32 $r14 $r12 + pop $r11 + pop $r12 + ret + +// calculate the number of ticks in the specified microsecond delay +// +// $r15 - current +// $r14 - us +// $r14 - ticks (return) +// $r0 - zero +ticks_from_us: + push $r12 + push $r11 + + /* simply multiply $us by HW_TICKS_PER_US */ + imm32($r13, HW_TICKS_PER_US) + call #mulu32_32_64 + mov b32 $r14 $r12 + + /* check if there wasn't any overflow */ + cmpu b32 $r11 0 + bra e #ticks_from_us_quit + + /* Overflow! */ + clear b32 $r14 + +ticks_from_us_quit: + pop $r11 + pop $r12 + ret + +// calculate the number of ticks in the specified microsecond delay // // $r15 - current // $r14 - ticks +// $r14 - us (return) +// $r0 - zero +ticks_to_us: + /* simply divide $ticks by HW_TICKS_PER_US */ + imm32($r13, HW_TICKS_PER_US) + div $r14 $r14 $r13 + + ret + +// request the current process be sent a message after a timeout expires +// +// $r15 - current +// $r14 - ticks (make sure it is < 2^31 to avoid any possible overflow) // $r0 - zero timer: + push $r9 + push $r8 + // interrupts off to prevent racing with timer isr bclr $flags ie0 @@ -255,13 +340,22 @@ timer: ld b32 $r8 D[$r15 + #proc_time] cmp b32 $r8 0 bra g #timer_done - st b32 D[$r15 + #proc_time] $r14 - // halt watchdog timer temporarily and check for a pending - // interrupt. if there's one already pending, we can just - // bail since the timer isr will queue the next soonest - // right after it's done + // halt watchdog timer temporarily + clear b32 $r8 nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8) + + // find out how much time elapsed since the last update + // of the watchdog and add this time to the wanted ticks + nv_iord($r8, NV_PPWR_WATCHDOG_TIME) + ld b32 $r9 D[$r0 + #time_prev] + sub b32 $r9 $r8 + add b32 $r14 $r9 + st b32 D[$r15 + #proc_time] $r14 + + // check for a pending interrupt. if there's one already + // pending, we can just bail since the timer isr will + // queue the next soonest right after it's done nv_iord($r8, NV_PPWR_INTR) and $r8 NV_PPWR_INTR_WATCHDOG bra nz #timer_enable @@ -272,10 +366,10 @@ timer: cmp b32 $r14 $r0 bra e #timer_reset cmp b32 $r14 $r8 - bra l #timer_done - timer_reset: - nv_iowr(NV_PPWR_WATCHDOG_TIME, $r14) - st b32 D[$r0 + #time_prev] $r14 + bra g #timer_enable + timer_reset: + nv_iowr(NV_PPWR_WATCHDOG_TIME, $r14) + st b32 D[$r0 + #time_prev] $r14 // re-enable the watchdog timer timer_enable: @@ -285,6 +379,9 @@ timer: // interrupts back on timer_done: bset $flags ie0 + + pop $r8 + pop $r9 ret // send message to another process @@ -371,6 +468,9 @@ send: // $r14 - process // $r0 - zero recv: + push $r9 + push $r8 + ld b32 $r8 D[$r14 + #proc_qget] ld b32 $r9 D[$r14 + #proc_qput] bclr $flags $p1 @@ -403,6 +503,8 @@ recv: bset $flags $p1 pop $r15 recv_done: + pop $r8 + pop $r9 ret init: diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/macros.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/macros.fuc index 5668e045bac1aab2d9d4660b6ea1df84287abc76..96fc984dafdc95159d910b1670a9bef826be8705 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/macros.fuc +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/macros.fuc @@ -250,3 +250,23 @@ */ st b32 D[$r0] reg /* */ clear b32 $r0 #endif + +#define st(size, addr, reg) /* +*/ movw $r0 addr /* +*/ st size D[$r0] reg /* +*/ clear b32 $r0 + +#define ld(size, reg, addr) /* +*/ movw $r0 addr /* +*/ ld size reg D[$r0] /* +*/ clear b32 $r0 + +// does a 64+64 -> 64 unsigned addition (C = A + B) +#define addu64(reg_a_c_hi, reg_a_c_lo, b_hi, b_lo) /* +*/ add b32 reg_a_c_lo b_lo /* +*/ adc b32 reg_a_c_hi b_hi + +// does a 64+64 -> 64 substraction (C = A - B) +#define subu64(reg_a_c_hi, reg_a_c_lo, b_hi, b_lo) /* +*/ sub b32 reg_a_c_lo b_lo /* +*/ sbb b32 reg_a_c_hi b_hi diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/memx.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/memx.fuc index d43741eccb11b66fd4106e233199c77516f0706b..e89789a53b80101721e1c5e6ea2589edfb642a94 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/memx.fuc +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/memx.fuc @@ -43,17 +43,23 @@ process(PROC_MEMX, #memx_init, #memx_recv) */ .b32 func memx_func_head: -handler(ENTER , 0x0001, 0x0000, #memx_func_enter) +handler(ENTER , 0x0000, 0x0000, #memx_func_enter) memx_func_next: handler(LEAVE , 0x0000, 0x0000, #memx_func_leave) handler(WR32 , 0x0000, 0x0002, #memx_func_wr32) handler(WAIT , 0x0004, 0x0000, #memx_func_wait) handler(DELAY , 0x0001, 0x0000, #memx_func_delay) +handler(VBLANK, 0x0001, 0x0000, #memx_func_wait_vblank) memx_func_tail: .equ #memx_func_size #memx_func_next - #memx_func_head .equ #memx_func_num (#memx_func_tail - #memx_func_head) / #memx_func_size +memx_ts_start: +.b32 0 +memx_ts_end: +.b32 0 + memx_data_head: .skip 0x0800 memx_data_tail: @@ -67,19 +73,44 @@ memx_data_tail: // // $r15 - current (memx) // $r4 - packet length -// +00: bitmask of heads to wait for vblank on // $r3 - opcode desciption // $r0 - zero memx_func_enter: +#if NVKM_PPWR_CHIPSET == GT215 + movw $r8 0x1610 + nv_rd32($r7, $r8) + imm32($r6, 0xfffffffc) + and $r7 $r6 + movw $r6 0x2 + or $r7 $r6 + nv_wr32($r8, $r7) +#else + movw $r6 0x001620 + imm32($r7, ~0x00000aa2); + nv_rd32($r8, $r6) + and $r8 $r7 + nv_wr32($r6, $r8) + + imm32($r7, ~0x00000001) + nv_rd32($r8, $r6) + and $r8 $r7 + nv_wr32($r6, $r8) + + movw $r6 0x0026f0 + nv_rd32($r8, $r6) + and $r8 $r7 + nv_wr32($r6, $r8) +#endif + mov $r6 NV_PPWR_OUTPUT_SET_FB_PAUSE nv_iowr(NV_PPWR_OUTPUT_SET, $r6) memx_func_enter_wait: nv_iord($r6, NV_PPWR_OUTPUT) and $r6 NV_PPWR_OUTPUT_FB_PAUSE bra z #memx_func_enter_wait - //XXX: TODO - ld b32 $r6 D[$r1 + 0x00] - add b32 $r1 0x04 + + nv_iord($r6, NV_PPWR_TIMER_LOW) + st b32 D[$r0 + #memx_ts_start] $r6 ret // description @@ -89,14 +120,93 @@ memx_func_enter: // $r3 - opcode desciption // $r0 - zero memx_func_leave: + nv_iord($r6, NV_PPWR_TIMER_LOW) + st b32 D[$r0 + #memx_ts_end] $r6 + mov $r6 NV_PPWR_OUTPUT_CLR_FB_PAUSE nv_iowr(NV_PPWR_OUTPUT_CLR, $r6) memx_func_leave_wait: nv_iord($r6, NV_PPWR_OUTPUT) and $r6 NV_PPWR_OUTPUT_FB_PAUSE bra nz #memx_func_leave_wait + +#if NVKM_PPWR_CHIPSET == GT215 + movw $r8 0x1610 + nv_rd32($r7, $r8) + imm32($r6, 0xffffffcc) + and $r7 $r6 + nv_wr32($r8, $r7) +#else + movw $r6 0x0026f0 + imm32($r7, 0x00000001) + nv_rd32($r8, $r6) + or $r8 $r7 + nv_wr32($r6, $r8) + + movw $r6 0x001620 + nv_rd32($r8, $r6) + or $r8 $r7 + nv_wr32($r6, $r8) + + imm32($r7, 0x00000aa2); + nv_rd32($r8, $r6) + or $r8 $r7 + nv_wr32($r6, $r8) +#endif + ret + +#if NVKM_PPWR_CHIPSET < GF119 +// description +// +// $r15 - current (memx) +// $r4 - packet length +// +00: head to wait for vblank on +// $r3 - opcode desciption +// $r0 - zero +memx_func_wait_vblank: + ld b32 $r6 D[$r1 + 0x00] + cmp b32 $r6 0x0 + bra z #memx_func_wait_vblank_head0 + cmp b32 $r6 0x1 + bra z #memx_func_wait_vblank_head1 + bra #memx_func_wait_vblank_fini + + memx_func_wait_vblank_head1: + movw $r7 0x20 + bra #memx_func_wait_vblank_0 + + memx_func_wait_vblank_head0: + movw $r7 0x8 + + memx_func_wait_vblank_0: + nv_iord($r6, NV_PPWR_INPUT) + and $r6 $r7 + bra nz #memx_func_wait_vblank_0 + + memx_func_wait_vblank_1: + nv_iord($r6, NV_PPWR_INPUT) + and $r6 $r7 + bra z #memx_func_wait_vblank_1 + + memx_func_wait_vblank_fini: + add b32 $r1 0x4 + ret + +#else + +// XXX: currently no-op +// +// $r15 - current (memx) +// $r4 - packet length +// +00: head to wait for vblank on +// $r3 - opcode desciption +// $r0 - zero +memx_func_wait_vblank: + add b32 $r1 0x4 ret +#endif + // description // // $r15 - current (memx) @@ -160,14 +270,17 @@ memx_exec: push $r13 mov b32 $r1 $r12 mov b32 $r2 $r11 + memx_exec_next: - // fetch the packet header, and locate opcode info + // fetch the packet header ld b32 $r3 D[$r1] add b32 $r1 4 - shr b32 $r4 $r3 16 - mulu $r3 #memx_func_size + extr $r4 $r3 16:31 + extr $r3 $r3 0:15 // execute the opcode handler + sub b32 $r3 1 + mulu $r3 #memx_func_size ld b32 $r5 D[$r3 + #memx_func_head + #memx_func] call $r5 @@ -176,6 +289,10 @@ memx_exec: bra l #memx_exec_next // send completion reply + ld b32 $r11 D[$r0 + #memx_ts_start] + ld b32 $r12 D[$r0 + #memx_ts_end] + sub b32 $r12 $r11 + nv_iord($r11, NV_PPWR_INPUT) pop $r13 pop $r14 call(send) diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc index 17a8a383d91a91929c2eebad9ef1f531861c6800..b439519ec866ac01a22a95e82ba124086560b9a5 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc @@ -23,6 +23,7 @@ */ #define NVKM_PPWR_CHIPSET GK208 +#define HW_TICKS_PER_US 324 #define NVKM_FALCON_PC24 #define NVKM_FALCON_UNSHIFTED_IO @@ -34,6 +35,7 @@ .section #nv108_pwr_data #define INCLUDE_PROC #include "kernel.fuc" +#include "arith.fuc" #include "host.fuc" #include "memx.fuc" #include "perf.fuc" @@ -44,6 +46,7 @@ #define INCLUDE_DATA #include "kernel.fuc" +#include "arith.fuc" #include "host.fuc" #include "memx.fuc" #include "perf.fuc" @@ -56,6 +59,7 @@ .section #nv108_pwr_code #define INCLUDE_CODE #include "kernel.fuc" +#include "arith.fuc" #include "host.fuc" #include "memx.fuc" #include "perf.fuc" diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc.h index 986495d533ddb30b9e842e8da6d443c91c0c6808..4d278a96b2bbb8459ff0517210967fe4d86dfc01 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc.h +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc.h @@ -24,8 +24,8 @@ uint32_t nv108_pwr_data[] = { 0x00000000, /* 0x0058: proc_list_head */ 0x54534f48, - 0x00000379, - 0x0000032a, + 0x00000453, + 0x00000404, 0x00000000, 0x00000000, 0x00000000, @@ -46,8 +46,8 @@ uint32_t nv108_pwr_data[] = { 0x00000000, 0x00000000, 0x584d454d, - 0x00000464, - 0x00000456, + 0x0000061c, + 0x0000060e, 0x00000000, 0x00000000, 0x00000000, @@ -68,8 +68,8 @@ uint32_t nv108_pwr_data[] = { 0x00000000, 0x00000000, 0x46524550, - 0x00000468, - 0x00000466, + 0x00000620, + 0x0000061e, 0x00000000, 0x00000000, 0x00000000, @@ -90,8 +90,8 @@ uint32_t nv108_pwr_data[] = { 0x00000000, 0x00000000, 0x5f433249, - 0x0000086c, - 0x00000713, + 0x00000a24, + 0x000008cb, 0x00000000, 0x00000000, 0x00000000, @@ -112,8 +112,8 @@ uint32_t nv108_pwr_data[] = { 0x00000000, 0x00000000, 0x54534554, - 0x0000088d, - 0x0000086e, + 0x00000a45, + 0x00000a26, 0x00000000, 0x00000000, 0x00000000, @@ -134,8 +134,8 @@ uint32_t nv108_pwr_data[] = { 0x00000000, 0x00000000, 0x454c4449, - 0x00000898, - 0x00000896, + 0x00000a50, + 0x00000a4e, 0x00000000, 0x00000000, 0x00000000, @@ -227,25 +227,31 @@ uint32_t nv108_pwr_data[] = { 0x00000000, 0x00000000, /* 0x0370: memx_func_head */ - 0x00010000, - 0x00000000, - 0x000003a9, -/* 0x037c: memx_func_next */ 0x00000001, 0x00000000, - 0x000003c7, + 0x00000483, +/* 0x037c: memx_func_next */ 0x00000002, + 0x00000000, + 0x00000500, + 0x00000003, 0x00000002, - 0x000003df, - 0x00040003, + 0x00000580, + 0x00040004, + 0x00000000, + 0x0000059d, + 0x00010005, + 0x00000000, + 0x000005b7, + 0x00010006, 0x00000000, - 0x000003fc, - 0x00010004, + 0x0000057b, +/* 0x03b8: memx_func_tail */ +/* 0x03b8: memx_ts_start */ 0x00000000, - 0x00000416, -/* 0x03ac: memx_func_tail */ -/* 0x03ac: memx_data_head */ +/* 0x03bc: memx_ts_end */ 0x00000000, +/* 0x03c0: memx_data_head */ 0x00000000, 0x00000000, 0x00000000, @@ -757,8 +763,9 @@ uint32_t nv108_pwr_data[] = { 0x00000000, 0x00000000, 0x00000000, -/* 0x0bac: memx_data_tail */ -/* 0x0bac: i2c_scl_map */ + 0x00000000, +/* 0x0bc0: memx_data_tail */ +/* 0x0bc0: i2c_scl_map */ 0x00000400, 0x00000800, 0x00001000, @@ -769,7 +776,7 @@ uint32_t nv108_pwr_data[] = { 0x00020000, 0x00040000, 0x00080000, -/* 0x0bd4: i2c_sda_map */ +/* 0x0be8: i2c_sda_map */ 0x00100000, 0x00200000, 0x00400000, @@ -781,10 +788,69 @@ uint32_t nv108_pwr_data[] = { 0x10000000, 0x20000000, 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, }; uint32_t nv108_pwr_code[] = { - 0x02910ef5, + 0x031c0ef5, /* 0x0004: rd32 */ 0xf607a040, 0x04bd000e, @@ -812,15 +878,18 @@ uint32_t nv108_pwr_code[] = { 0x7000d4f1, 0xf8f61bf4, /* 0x005d: nsec */ - 0xcf2c0800, -/* 0x0062: nsec_loop */ + 0xf990f900, + 0xcf2c0880, +/* 0x0066: nsec_loop */ 0x2c090088, 0xbb0099cf, 0x9ea60298, - 0xf8f61ef4, -/* 0x0071: wait */ - 0xcf2c0800, -/* 0x0076: wait_loop */ + 0xfcf61ef4, + 0xf890fc80, +/* 0x0079: wait */ + 0xf990f900, + 0xcf2c0880, +/* 0x0082: wait_loop */ 0xeeb20088, 0x0000047e, 0xadfddab2, @@ -828,28 +897,29 @@ uint32_t nv108_pwr_code[] = { 0x2c09100b, 0xbb0099cf, 0x9ba60298, -/* 0x0093: wait_done */ - 0xf8e61ef4, -/* 0x0095: intr_watchdog */ +/* 0x009f: wait_done */ + 0xfce61ef4, + 0xf890fc80, +/* 0x00a5: intr_watchdog */ 0x03e99800, 0xf40096b0, 0x0a98280b, 0x029abb9a, 0x0d0e1cf4, - 0x01de7e01, + 0x02617e01, 0xf494bd00, -/* 0x00b2: intr_watchdog_next_time */ +/* 0x00c2: intr_watchdog_next_time */ 0x0a98140e, 0x00a6b09b, 0xa6080bf4, 0x061cf49a, -/* 0x00c0: intr_watchdog_next_time_set */ -/* 0x00c3: intr_watchdog_next_proc */ +/* 0x00d0: intr_watchdog_next_time_set */ +/* 0x00d3: intr_watchdog_next_proc */ 0xb59b09b5, 0xe0b603e9, 0x68e6b158, 0xc81bf402, -/* 0x00d2: intr */ +/* 0x00e2: intr */ 0x00f900f8, 0x80f904bd, 0xa0f990f9, @@ -865,13 +935,13 @@ uint32_t nv108_pwr_code[] = { 0xc40088cf, 0x0bf40289, 0x9b00b51f, - 0x957e580e, + 0xa57e580e, 0x09980000, 0x0096b09b, 0x000d0bf4, 0x0009f634, 0x09b504bd, -/* 0x0125: intr_skip_watchdog */ +/* 0x0135: intr_skip_watchdog */ 0x0089e49a, 0x360bf408, 0xcf068849, @@ -881,20 +951,20 @@ uint32_t nv108_pwr_code[] = { 0xc0f900cc, 0xf14f484e, 0x0d5453e3, - 0x023f7e00, + 0x02c27e00, 0x40c0fc00, 0x0cf604c0, -/* 0x0157: intr_subintr_skip_fifo */ +/* 0x0167: intr_subintr_skip_fifo */ 0x4004bd00, 0x09f60688, -/* 0x015f: intr_skip_subintr */ +/* 0x016f: intr_skip_subintr */ 0xc404bd00, 0x0bf42089, 0xbfa4f107, -/* 0x0169: intr_skip_pause */ +/* 0x0179: intr_skip_pause */ 0x4089c4ff, 0xf1070bf4, -/* 0x0173: intr_skip_user0 */ +/* 0x0183: intr_skip_user0 */ 0x00ffbfa4, 0x0008f604, 0x80fc04bd, @@ -904,304 +974,417 @@ uint32_t nv108_pwr_code[] = { 0xfca0fcb0, 0xfc80fc90, 0x0032f400, -/* 0x0196: timer */ - 0x32f401f8, - 0x03f89810, - 0xf40086b0, - 0xfeb53a1c, - 0xf6380003, +/* 0x01a6: ticks_from_ns */ + 0xc0f901f8, + 0xd7f1b0f9, + 0xd3f00144, + 0x7721f500, + 0xe8ccec03, + 0x00b4b003, + 0xec120bf4, + 0xf103e8ee, + 0xf00144d7, + 0x21f500d3, +/* 0x01ce: ticks_from_ns_quit */ + 0xceb20377, + 0xc0fcb0fc, +/* 0x01d6: ticks_from_us */ + 0xc0f900f8, + 0xd7f1b0f9, + 0xd3f00144, + 0x7721f500, + 0xb0ceb203, + 0x0bf400b4, +/* 0x01ef: ticks_from_us_quit */ + 0xfce4bd05, + 0xf8c0fcb0, +/* 0x01f5: ticks_to_us */ + 0x44d7f100, + 0x00d3f001, + 0xf8ecedff, +/* 0x0201: timer */ + 0xf990f900, + 0x1032f480, + 0xb003f898, + 0x1cf40086, + 0x0084bd4a, + 0x0008f638, + 0x340804bd, + 0x980088cf, + 0x98bb9a09, + 0x00e9bb02, + 0x0803feb5, + 0x0088cf08, + 0xf40284f0, + 0x34081c1b, + 0xa60088cf, + 0x080bf4e0, + 0x1cf4e8a6, +/* 0x0245: timer_reset */ + 0xf634000d, + 0x04bd000e, +/* 0x024f: timer_enable */ + 0x089a0eb5, + 0xf6380001, 0x04bd0008, - 0x88cf0808, - 0x0284f000, - 0x081c1bf4, - 0x0088cf34, - 0x0bf4e0a6, - 0xf4e8a608, -/* 0x01c6: timer_reset */ - 0x3400161e, - 0xbd000ef6, - 0x9a0eb504, -/* 0x01d0: timer_enable */ - 0x38000108, - 0xbd0008f6, -/* 0x01d9: timer_done */ - 0x1031f404, -/* 0x01de: send_proc */ - 0x80f900f8, - 0xe89890f9, - 0x04e99805, - 0xa60486f0, - 0x2a0bf489, - 0x940398c4, - 0x80b60488, - 0x008ebb18, - 0xb500fa98, - 0x8db5008a, - 0x028cb501, - 0xb6038bb5, - 0x94f00190, - 0x04e9b507, -/* 0x0217: send_done */ - 0xfc0231f4, - 0xf880fc90, -/* 0x021d: find */ - 0x0880f900, - 0x0131f458, -/* 0x0224: find_loop */ - 0xa6008a98, - 0x100bf4ae, - 0xb15880b6, - 0xf4026886, - 0x32f4f11b, -/* 0x0239: find_done */ - 0xfc8eb201, -/* 0x023f: send */ - 0x7e00f880, - 0xf400021d, - 0x00f89b01, -/* 0x0248: recv */ - 0x9805e898, - 0x32f404e9, - 0xf489a601, - 0x89c43c0b, - 0x0180b603, - 0xb50784f0, - 0xea9805e8, - 0xfef0f902, - 0xf0f9018f, - 0x9994efb2, - 0x00e9bb04, - 0x9818e0b6, - 0xec9803eb, - 0x01ed9802, - 0xf900ee98, - 0xfef0fca5, - 0x31f400f8, -/* 0x028f: recv_done */ - 0xf8f0fc01, -/* 0x0291: init */ - 0x01084100, - 0xe70011cf, - 0xb6010911, - 0x14fe0814, - 0x00e04100, - 0x000013f0, - 0x0001f61c, - 0xff0104bd, - 0x01f61400, - 0x0104bd00, - 0x0015f102, - 0xf6100008, - 0x04bd0001, - 0xf000d241, - 0x10fe0013, - 0x1031f400, - 0x38000101, +/* 0x0258: timer_done */ + 0xfc1031f4, + 0xf890fc80, +/* 0x0261: send_proc */ + 0xf980f900, + 0x05e89890, + 0xf004e998, + 0x89a60486, + 0xc42a0bf4, + 0x88940398, + 0x1880b604, + 0x98008ebb, + 0x8ab500fa, + 0x018db500, + 0xb5028cb5, + 0x90b6038b, + 0x0794f001, + 0xf404e9b5, +/* 0x029a: send_done */ + 0x90fc0231, + 0x00f880fc, +/* 0x02a0: find */ + 0x580880f9, +/* 0x02a7: find_loop */ + 0x980131f4, + 0xaea6008a, + 0xb6100bf4, + 0x86b15880, + 0x1bf40268, + 0x0132f4f1, +/* 0x02bc: find_done */ + 0x80fc8eb2, +/* 0x02c2: send */ + 0xa07e00f8, + 0x01f40002, +/* 0x02cb: recv */ + 0xf900f89b, + 0x9880f990, + 0xe99805e8, + 0x0132f404, + 0x0bf489a6, + 0x0389c43c, + 0xf00180b6, + 0xe8b50784, + 0x02ea9805, + 0x8ffef0f9, + 0xb2f0f901, + 0x049994ef, + 0xb600e9bb, + 0xeb9818e0, + 0x02ec9803, + 0x9801ed98, + 0xa5f900ee, + 0xf8fef0fc, + 0x0131f400, +/* 0x0316: recv_done */ + 0x80fcf0fc, + 0x00f890fc, +/* 0x031c: init */ + 0xcf010841, + 0x11e70011, + 0x14b60109, + 0x0014fe08, + 0xf000e041, + 0x1c000013, 0xbd0001f6, -/* 0x02db: init_proc */ - 0x98580f04, - 0x16b001f1, - 0xfa0bf400, - 0xf0b615f9, - 0xf20ef458, -/* 0x02ec: host_send */ - 0xcf04b041, - 0xa0420011, - 0x0022cf04, - 0x0bf412a6, - 0x071ec42e, - 0xb704ee94, - 0x980270e0, - 0xec9803eb, - 0x01ed9802, - 0x7e00ee98, - 0xb600023f, - 0x1ec40110, - 0x04b0400f, - 0xbd000ef6, - 0xc70ef404, -/* 0x0328: host_send_done */ -/* 0x032a: host_recv */ - 0x494100f8, - 0x5413f14e, - 0xf4e1a652, -/* 0x0336: host_recv_wait */ - 0xcc41b90b, - 0x0011cf04, - 0xcf04c842, - 0x16f00022, - 0xf412a608, - 0x23c4ef0b, - 0x0434b607, - 0x02f030b7, - 0xb5033bb5, - 0x3db5023c, - 0x003eb501, - 0xf00120b6, - 0xc8400f24, - 0x0002f604, - 0x400204bd, - 0x02f60000, - 0xf804bd00, -/* 0x0379: host_init */ - 0x00804100, - 0xf11014b6, - 0x40027015, - 0x01f604d0, + 0x00ff0104, + 0x0001f614, + 0x020104bd, + 0x080015f1, + 0x01f61000, 0x4104bd00, + 0x13f000e2, + 0x0010fe00, + 0x011031f4, + 0xf6380001, + 0x04bd0001, +/* 0x0366: init_proc */ + 0xf198580f, + 0x0016b001, + 0xf9fa0bf4, + 0x58f0b615, +/* 0x0377: mulu32_32_64 */ + 0xf9f20ef4, + 0xf920f910, + 0x9540f930, + 0xd29510e1, + 0xbdc4bd10, + 0xc0edffb4, + 0xb2301dff, + 0xff34f134, + 0x1034b6ff, + 0xbb1045b6, + 0xb4bb00c3, + 0x30e2ff01, + 0x34f134b2, + 0x34b6ffff, + 0x1045b610, + 0xbb00c3bb, + 0x12ff01b4, + 0x00b3bb30, + 0x30fc40fc, + 0x10fc20fc, +/* 0x03c6: host_send */ + 0xb04100f8, + 0x0011cf04, + 0xcf04a042, + 0x12a60022, + 0xc42e0bf4, + 0xee94071e, + 0x70e0b704, + 0x03eb9802, + 0x9802ec98, + 0xee9801ed, + 0x02c27e00, + 0x0110b600, + 0x400f1ec4, + 0x0ef604b0, + 0xf404bd00, +/* 0x0402: host_send_done */ + 0x00f8c70e, +/* 0x0404: host_recv */ + 0xf14e4941, + 0xa6525413, + 0xb90bf4e1, +/* 0x0410: host_recv_wait */ + 0xcf04cc41, + 0xc8420011, + 0x0022cf04, + 0xa60816f0, + 0xef0bf412, + 0xb60723c4, + 0x30b70434, + 0x3bb502f0, + 0x023cb503, + 0xb5013db5, + 0x20b6003e, + 0x0f24f001, + 0xf604c840, + 0x04bd0002, + 0x00004002, + 0xbd0002f6, +/* 0x0453: host_init */ + 0x4100f804, 0x14b60080, - 0xf015f110, - 0x04dc4002, + 0x7015f110, + 0x04d04002, + 0xbd0001f6, + 0x00804104, + 0xf11014b6, + 0x4002f015, + 0x01f604dc, + 0x0104bd00, + 0x04c44001, 0xbd0001f6, - 0x40010104, - 0x01f604c4, - 0xf804bd00, -/* 0x03a9: memx_func_enter */ - 0x40040600, - 0x06f607e0, -/* 0x03b3: memx_func_enter_wait */ - 0x4604bd00, - 0x66cf07c0, - 0x0464f000, - 0x98f70bf4, - 0x10b60016, -/* 0x03c7: memx_func_leave */ - 0x0600f804, - 0x07e44004, - 0xbd0006f6, -/* 0x03d1: memx_func_leave_wait */ - 0x07c04604, - 0xf00066cf, - 0x1bf40464, -/* 0x03df: memx_func_wr32 */ - 0x9800f8f7, - 0x15980016, - 0x0810b601, - 0x50f960f9, +/* 0x0483: memx_func_enter */ + 0xf100f804, + 0xf1162067, + 0xf1f55d77, + 0xb2ffff73, + 0x00047e6e, + 0xfdd8b200, + 0x60f90487, + 0xd0fc80f9, + 0x2e7ee0fc, + 0x77f10000, + 0x73f1fffe, + 0x6eb2ffff, + 0x0000047e, + 0x87fdd8b2, + 0xf960f904, + 0xfcd0fc80, + 0x002e7ee0, + 0xf067f100, + 0x7e6eb226, + 0xb2000004, + 0x0487fdd8, + 0x80f960f9, 0xe0fcd0fc, 0x00002e7e, - 0xf40242b6, - 0x00f8e81b, -/* 0x03fc: memx_func_wait */ - 0x88cf2c08, - 0x001e9800, - 0x98011d98, - 0x1b98021c, - 0x1010b603, - 0x0000717e, -/* 0x0416: memx_func_delay */ - 0x1e9800f8, - 0x0410b600, - 0x00005d7e, -/* 0x0422: memx_exec */ - 0xe0f900f8, - 0xc1b2d0f9, -/* 0x042a: memx_exec_next */ - 0x1398b2b2, - 0x0410b600, - 0xf0103495, - 0x35980c30, - 0xa655f9de, - 0xed1ef412, + 0xe0400406, + 0x0006f607, +/* 0x04ea: memx_func_enter_wait */ + 0xc04604bd, + 0x0066cf07, + 0xf40464f0, + 0x2c06f70b, + 0xb50066cf, + 0x00f8ee06, +/* 0x0500: memx_func_leave */ + 0x66cf2c06, + 0xef06b500, + 0xe4400406, + 0x0006f607, +/* 0x0512: memx_func_leave_wait */ + 0xc04604bd, + 0x0066cf07, + 0xf40464f0, + 0x67f1f71b, + 0x77f126f0, + 0x73f00001, + 0x7e6eb200, + 0xb2000004, + 0x0587fdd8, + 0x80f960f9, 0xe0fcd0fc, - 0x00023f7e, -/* 0x044a: memx_info */ - 0xac4c00f8, + 0x00002e7e, + 0x162067f1, + 0x047e6eb2, + 0xd8b20000, + 0xf90587fd, + 0xfc80f960, + 0x7ee0fcd0, + 0xf100002e, + 0xf00aa277, + 0x6eb20073, + 0x0000047e, + 0x87fdd8b2, + 0xf960f905, + 0xfcd0fc80, + 0x002e7ee0, +/* 0x057b: memx_func_wait_vblank */ + 0xb600f800, + 0x00f80410, +/* 0x0580: memx_func_wr32 */ + 0x98001698, + 0x10b60115, + 0xf960f908, + 0xfcd0fc50, + 0x002e7ee0, + 0x0242b600, + 0xf8e81bf4, +/* 0x059d: memx_func_wait */ + 0xcf2c0800, + 0x1e980088, + 0x011d9800, + 0x98021c98, + 0x10b6031b, + 0x00797e10, +/* 0x05b7: memx_func_delay */ + 0x9800f800, + 0x10b6001e, + 0x005d7e04, +/* 0x05c3: memx_exec */ + 0xf900f800, + 0xb2d0f9e0, +/* 0x05cb: memx_exec_next */ + 0x98b2b2c1, + 0x10b60013, + 0xf034e704, + 0xe033e701, + 0x0132b601, + 0x980c30f0, + 0x55f9de35, + 0x1ef412a6, + 0xee0b98e5, + 0xbbef0c98, + 0xc44b02cb, + 0x00bbcf07, + 0xe0fcd0fc, + 0x0002c27e, +/* 0x0602: memx_info */ + 0xc04c00f8, 0x08004b03, - 0x00023f7e, -/* 0x0456: memx_recv */ + 0x0002c27e, +/* 0x060e: memx_recv */ 0xd6b000f8, - 0xc90bf401, + 0xb20bf401, 0xf400d6b0, 0x00f8eb0b, -/* 0x0464: memx_init */ -/* 0x0466: perf_recv */ +/* 0x061c: memx_init */ +/* 0x061e: perf_recv */ 0x00f800f8, -/* 0x0468: perf_init */ -/* 0x046a: i2c_drive_scl */ +/* 0x0620: perf_init */ +/* 0x0622: i2c_drive_scl */ 0x36b000f8, 0x0d0bf400, 0xf607e040, 0x04bd0001, -/* 0x047a: i2c_drive_scl_lo */ +/* 0x0632: i2c_drive_scl_lo */ 0xe44000f8, 0x0001f607, 0x00f804bd, -/* 0x0484: i2c_drive_sda */ +/* 0x063c: i2c_drive_sda */ 0xf40036b0, 0xe0400d0b, 0x0002f607, 0x00f804bd, -/* 0x0494: i2c_drive_sda_lo */ +/* 0x064c: i2c_drive_sda_lo */ 0xf607e440, 0x04bd0002, -/* 0x049e: i2c_sense_scl */ +/* 0x0656: i2c_sense_scl */ 0x32f400f8, 0x07c44301, 0xfd0033cf, 0x0bf40431, 0x0131f406, -/* 0x04b0: i2c_sense_scl_done */ -/* 0x04b2: i2c_sense_sda */ +/* 0x0668: i2c_sense_scl_done */ +/* 0x066a: i2c_sense_sda */ 0x32f400f8, 0x07c44301, 0xfd0033cf, 0x0bf40432, 0x0131f406, -/* 0x04c4: i2c_sense_sda_done */ -/* 0x04c6: i2c_raise_scl */ +/* 0x067c: i2c_sense_sda_done */ +/* 0x067e: i2c_raise_scl */ 0x40f900f8, 0x03089844, - 0x046a7e01, -/* 0x04d1: i2c_raise_scl_wait */ + 0x06227e01, +/* 0x0689: i2c_raise_scl_wait */ 0x03e84e00, 0x00005d7e, - 0x00049e7e, + 0x0006567e, 0xb60901f4, 0x1bf40142, -/* 0x04e5: i2c_raise_scl_done */ +/* 0x069d: i2c_raise_scl_done */ 0xf840fcef, -/* 0x04e9: i2c_start */ - 0x049e7e00, +/* 0x06a1: i2c_start */ + 0x06567e00, 0x0d11f400, - 0x0004b27e, + 0x00066a7e, 0xf40611f4, -/* 0x04fa: i2c_start_rep */ +/* 0x06b2: i2c_start_rep */ 0x00032e0e, - 0x00046a7e, - 0x847e0103, - 0x76bb0004, + 0x0006227e, + 0x3c7e0103, + 0x76bb0006, 0x0465b600, 0x659450f9, 0x0256bb04, 0x75fd50bd, 0x7e50fc04, - 0xb60004c6, + 0xb600067e, 0x11f40464, -/* 0x0525: i2c_start_send */ +/* 0x06dd: i2c_start_send */ 0x7e00031d, - 0x4e000484, + 0x4e00063c, 0x5d7e1388, 0x00030000, - 0x00046a7e, + 0x0006227e, 0x7e13884e, -/* 0x053f: i2c_start_out */ +/* 0x06f7: i2c_start_out */ 0xf800005d, -/* 0x0541: i2c_stop */ +/* 0x06f9: i2c_stop */ 0x7e000300, - 0x0300046a, - 0x04847e00, + 0x03000622, + 0x063c7e00, 0x03e84e00, 0x00005d7e, - 0x6a7e0103, - 0x884e0004, + 0x227e0103, + 0x884e0006, 0x005d7e13, 0x7e010300, - 0x4e000484, + 0x4e00063c, 0x5d7e1388, 0x00f80000, -/* 0x0570: i2c_bitw */ - 0x0004847e, +/* 0x0728: i2c_bitw */ + 0x00063c7e, 0x7e03e84e, 0xbb00005d, 0x65b60076, @@ -1209,44 +1392,44 @@ uint32_t nv108_pwr_code[] = { 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x0004c67e, + 0x00067e7e, 0xf40464b6, 0x884e1711, 0x005d7e13, 0x7e000300, - 0x4e00046a, + 0x4e000622, 0x5d7e1388, -/* 0x05ae: i2c_bitw_out */ +/* 0x0766: i2c_bitw_out */ 0x00f80000, -/* 0x05b0: i2c_bitr */ - 0x847e0103, - 0xe84e0004, +/* 0x0768: i2c_bitr */ + 0x3c7e0103, + 0xe84e0006, 0x005d7e03, 0x0076bb00, 0xf90465b6, 0x04659450, 0xbd0256bb, 0x0475fd50, - 0xc67e50fc, - 0x64b60004, + 0x7e7e50fc, + 0x64b60006, 0x1a11f404, - 0x0004b27e, - 0x6a7e0003, - 0x884e0004, + 0x00066a7e, + 0x227e0003, + 0x884e0006, 0x005d7e13, 0x013cf000, -/* 0x05f3: i2c_bitr_done */ +/* 0x07ab: i2c_bitr_done */ 0xf80131f4, -/* 0x05f5: i2c_get_byte */ +/* 0x07ad: i2c_get_byte */ 0x04000500, -/* 0x05f9: i2c_get_byte_next */ +/* 0x07b1: i2c_get_byte_next */ 0x0154b608, 0xb60076bb, 0x50f90465, 0xbb046594, 0x50bd0256, 0xfc0475fd, - 0x05b07e50, + 0x07687e50, 0x0464b600, 0xfd2a11f4, 0x42b60553, @@ -1257,11 +1440,11 @@ uint32_t nv108_pwr_code[] = { 0x0256bb04, 0x75fd50bd, 0x7e50fc04, - 0xb6000570, -/* 0x0642: i2c_get_byte_done */ + 0xb6000728, +/* 0x07fa: i2c_get_byte_done */ 0x00f80464, -/* 0x0644: i2c_put_byte */ -/* 0x0646: i2c_put_byte_next */ +/* 0x07fc: i2c_put_byte */ +/* 0x07fe: i2c_put_byte_next */ 0x42b60804, 0x3854ff01, 0xb60076bb, @@ -1269,7 +1452,7 @@ uint32_t nv108_pwr_code[] = { 0xbb046594, 0x50bd0256, 0xfc0475fd, - 0x05707e50, + 0x07287e50, 0x0464b600, 0xb03411f4, 0x1bf40046, @@ -1278,21 +1461,21 @@ uint32_t nv108_pwr_code[] = { 0x04659450, 0xbd0256bb, 0x0475fd50, - 0xb07e50fc, - 0x64b60005, + 0x687e50fc, + 0x64b60007, 0x0f11f404, 0xb00076bb, 0x1bf40136, 0x0132f406, -/* 0x069c: i2c_put_byte_done */ -/* 0x069e: i2c_addr */ +/* 0x0854: i2c_put_byte_done */ +/* 0x0856: i2c_addr */ 0x76bb00f8, 0x0465b600, 0x659450f9, 0x0256bb04, 0x75fd50bd, 0x7e50fc04, - 0xb60004e9, + 0xb60006a1, 0x11f40464, 0x2ec3e729, 0x0134b601, @@ -1302,32 +1485,32 @@ uint32_t nv108_pwr_code[] = { 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x0006447e, -/* 0x06e3: i2c_addr_done */ + 0x0007fc7e, +/* 0x089b: i2c_addr_done */ 0xf80464b6, -/* 0x06e5: i2c_acquire_addr */ +/* 0x089d: i2c_acquire_addr */ 0xf8cec700, 0xb705e4b6, 0xf8d014e0, -/* 0x06f1: i2c_acquire */ - 0x06e57e00, +/* 0x08a9: i2c_acquire */ + 0x089d7e00, 0x00047e00, 0x03d9f000, 0x00002e7e, -/* 0x0702: i2c_release */ - 0xe57e00f8, - 0x047e0006, +/* 0x08ba: i2c_release */ + 0x9d7e00f8, + 0x047e0008, 0xdaf00000, 0x002e7e03, -/* 0x0713: i2c_recv */ +/* 0x08cb: i2c_recv */ 0xf400f800, 0xc1c70132, 0x0214b6f8, 0xf52816b0, 0xb801371f, - 0x000bd413, + 0x000be813, 0xb8003298, - 0x000bac13, + 0x000bc013, 0xf4003198, 0xd0f90231, 0xd0f9e0f9, @@ -1339,7 +1522,7 @@ uint32_t nv108_pwr_code[] = { 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x0006f17e, + 0x0008a97e, 0xfc0464b6, 0x00d6b0d0, 0x00b01bf5, @@ -1349,7 +1532,7 @@ uint32_t nv108_pwr_code[] = { 0x0256bb04, 0x75fd50bd, 0x7e50fc04, - 0xb600069e, + 0xb6000856, 0x11f50464, 0xc5c700cc, 0x0076bbe0, @@ -1357,8 +1540,8 @@ uint32_t nv108_pwr_code[] = { 0x04659450, 0xbd0256bb, 0x0475fd50, - 0x447e50fc, - 0x64b60006, + 0xfc7e50fc, + 0x64b60007, 0xa911f504, 0xbb010500, 0x65b60076, @@ -1366,7 +1549,7 @@ uint32_t nv108_pwr_code[] = { 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x00069e7e, + 0x0008567e, 0xf50464b6, 0xbb008711, 0x65b60076, @@ -1374,7 +1557,7 @@ uint32_t nv108_pwr_code[] = { 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x0005f57e, + 0x0007ad7e, 0xf40464b6, 0x5bcb6711, 0x0076bbe0, @@ -1382,37 +1565,37 @@ uint32_t nv108_pwr_code[] = { 0x04659450, 0xbd0256bb, 0x0475fd50, - 0x417e50fc, - 0x64b60005, + 0xf97e50fc, + 0x64b60006, 0xbd5bb204, 0x410ef474, -/* 0x0818: i2c_recv_not_rd08 */ +/* 0x09d0: i2c_recv_not_rd08 */ 0xf401d6b0, 0x00053b1b, - 0x00069e7e, + 0x0008567e, 0xc73211f4, - 0x447ee0c5, - 0x11f40006, + 0xfc7ee0c5, + 0x11f40007, 0x7e000528, - 0xf400069e, + 0xf4000856, 0xb5c71f11, - 0x06447ee0, + 0x07fc7ee0, 0x1511f400, - 0x0005417e, + 0x0006f97e, 0xc5c774bd, 0x091bf408, 0xf40232f4, -/* 0x0856: i2c_recv_not_wr08 */ -/* 0x0856: i2c_recv_done */ +/* 0x0a0e: i2c_recv_not_wr08 */ +/* 0x0a0e: i2c_recv_done */ 0xcec7030e, - 0x07027ef8, + 0x08ba7ef8, 0xfce0fc00, 0x0912f4d0, - 0x3f7e7cb2, -/* 0x086a: i2c_recv_exit */ + 0xc27e7cb2, +/* 0x0a22: i2c_recv_exit */ 0x00f80002, -/* 0x086c: i2c_init */ -/* 0x086e: test_recv */ +/* 0x0a24: i2c_init */ +/* 0x0a26: test_recv */ 0x584100f8, 0x0011cf04, 0x400110b6, @@ -1420,28 +1603,28 @@ uint32_t nv108_pwr_code[] = { 0xf104bd00, 0xf1d900e7, 0x7e134fe3, - 0xf8000196, -/* 0x088d: test_init */ + 0xf8000201, +/* 0x0a45: test_init */ 0x08004e00, - 0x0001967e, -/* 0x0896: idle_recv */ + 0x0002017e, +/* 0x0a4e: idle_recv */ 0x00f800f8, -/* 0x0898: idle */ +/* 0x0a50: idle */ 0x410031f4, 0x11cf0454, 0x0110b600, 0xf6045440, 0x04bd0001, -/* 0x08ac: idle_loop */ +/* 0x0a64: idle_loop */ 0x32f45801, -/* 0x08b1: idle_proc */ -/* 0x08b1: idle_proc_exec */ +/* 0x0a69: idle_proc */ +/* 0x0a69: idle_proc_exec */ 0xb210f902, - 0x02487e1e, + 0x02cb7e1e, 0xf410fc00, 0x31f40911, 0xf00ef402, -/* 0x08c4: idle_proc_next */ +/* 0x0a7c: idle_proc_next */ 0xa65810b6, 0xe81bf41f, 0xf4e002f4, @@ -1457,4 +1640,22 @@ uint32_t nv108_pwr_code[] = { 0x00000000, 0x00000000, 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc index 6744fcc0615160e657227285a70fc2c201bb7cb9..daa06c1c655e206dbff9ea20b7768d0b7d1e502e 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc @@ -23,6 +23,7 @@ */ #define NVKM_PPWR_CHIPSET GT215 +#define HW_TICKS_PER_US 203 // should be 202.5 //#define NVKM_FALCON_PC24 //#define NVKM_FALCON_UNSHIFTED_IO @@ -34,6 +35,7 @@ .section #nva3_pwr_data #define INCLUDE_PROC #include "kernel.fuc" +#include "arith.fuc" #include "host.fuc" #include "memx.fuc" #include "perf.fuc" @@ -44,6 +46,7 @@ #define INCLUDE_DATA #include "kernel.fuc" +#include "arith.fuc" #include "host.fuc" #include "memx.fuc" #include "perf.fuc" @@ -56,6 +59,7 @@ .section #nva3_pwr_code #define INCLUDE_CODE #include "kernel.fuc" +#include "arith.fuc" #include "host.fuc" #include "memx.fuc" #include "perf.fuc" diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc.h index e087ce3041beb51069821677b7b58c260acf5863..64e97baabc3c4d71850641df7a3652d94703bbb4 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc.h +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc.h @@ -24,8 +24,8 @@ uint32_t nva3_pwr_data[] = { 0x00000000, /* 0x0058: proc_list_head */ 0x54534f48, - 0x00000430, - 0x000003cd, + 0x00000512, + 0x000004af, 0x00000000, 0x00000000, 0x00000000, @@ -46,8 +46,8 @@ uint32_t nva3_pwr_data[] = { 0x00000000, 0x00000000, 0x584d454d, - 0x00000542, - 0x00000534, + 0x000006e0, + 0x000006d2, 0x00000000, 0x00000000, 0x00000000, @@ -68,8 +68,8 @@ uint32_t nva3_pwr_data[] = { 0x00000000, 0x00000000, 0x46524550, - 0x00000546, - 0x00000544, + 0x000006e4, + 0x000006e2, 0x00000000, 0x00000000, 0x00000000, @@ -90,8 +90,8 @@ uint32_t nva3_pwr_data[] = { 0x00000000, 0x00000000, 0x5f433249, - 0x00000976, - 0x00000819, + 0x00000b14, + 0x000009b7, 0x00000000, 0x00000000, 0x00000000, @@ -112,8 +112,8 @@ uint32_t nva3_pwr_data[] = { 0x00000000, 0x00000000, 0x54534554, - 0x0000099f, - 0x00000978, + 0x00000b3d, + 0x00000b16, 0x00000000, 0x00000000, 0x00000000, @@ -134,8 +134,8 @@ uint32_t nva3_pwr_data[] = { 0x00000000, 0x00000000, 0x454c4449, - 0x000009ab, - 0x000009a9, + 0x00000b49, + 0x00000b47, 0x00000000, 0x00000000, 0x00000000, @@ -227,25 +227,31 @@ uint32_t nva3_pwr_data[] = { 0x00000000, 0x00000000, /* 0x0370: memx_func_head */ - 0x00010000, - 0x00000000, - 0x0000046f, -/* 0x037c: memx_func_next */ 0x00000001, 0x00000000, - 0x00000496, + 0x00000551, +/* 0x037c: memx_func_next */ 0x00000002, + 0x00000000, + 0x000005a8, + 0x00000003, 0x00000002, - 0x000004b7, - 0x00040003, + 0x0000063a, + 0x00040004, + 0x00000000, + 0x00000656, + 0x00010005, + 0x00000000, + 0x00000673, + 0x00010006, 0x00000000, - 0x000004d3, - 0x00010004, + 0x000005f8, +/* 0x03b8: memx_func_tail */ +/* 0x03b8: memx_ts_start */ 0x00000000, - 0x000004f0, -/* 0x03ac: memx_func_tail */ -/* 0x03ac: memx_data_head */ +/* 0x03bc: memx_ts_end */ 0x00000000, +/* 0x03c0: memx_data_head */ 0x00000000, 0x00000000, 0x00000000, @@ -757,8 +763,9 @@ uint32_t nva3_pwr_data[] = { 0x00000000, 0x00000000, 0x00000000, -/* 0x0bac: memx_data_tail */ -/* 0x0bac: i2c_scl_map */ + 0x00000000, +/* 0x0bc0: memx_data_tail */ +/* 0x0bc0: i2c_scl_map */ 0x00001000, 0x00004000, 0x00010000, @@ -769,7 +776,7 @@ uint32_t nva3_pwr_data[] = { 0x01000000, 0x04000000, 0x10000000, -/* 0x0bd4: i2c_sda_map */ +/* 0x0be8: i2c_sda_map */ 0x00002000, 0x00008000, 0x00020000, @@ -780,7 +787,7 @@ uint32_t nva3_pwr_data[] = { 0x02000000, 0x08000000, 0x20000000, -/* 0x0bfc: i2c_ctrl */ +/* 0x0c10: i2c_ctrl */ 0x0000e138, 0x0000e150, 0x0000e168, @@ -841,15 +848,10 @@ uint32_t nva3_pwr_data[] = { 0x00000000, 0x00000000, 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, }; uint32_t nva3_pwr_code[] = { - 0x030d0ef5, + 0x039e0ef5, /* 0x0004: rd32 */ 0x07a007f1, 0xd00604b6, @@ -885,19 +887,22 @@ uint32_t nva3_pwr_code[] = { 0xd4f100dd, 0x1bf47000, /* 0x007f: nsec */ - 0xf000f8f2, + 0xf900f8f2, + 0xf080f990, 0x84b62c87, 0x0088cf06, -/* 0x0088: nsec_loop */ +/* 0x008c: nsec_loop */ 0xb62c97f0, 0x99cf0694, 0x0298bb00, 0xf4069eb8, - 0x00f8f11e, -/* 0x009c: wait */ + 0x80fcf11e, + 0x00f890fc, +/* 0x00a4: wait */ + 0x80f990f9, 0xb62c87f0, 0x88cf0684, -/* 0x00a5: wait_loop */ +/* 0x00b1: wait_loop */ 0x02eeb900, 0xb90421f4, 0xadfd02da, @@ -907,28 +912,29 @@ uint32_t nva3_pwr_code[] = { 0x0099cf06, 0xb80298bb, 0x1ef4069b, -/* 0x00c9: wait_done */ -/* 0x00cb: intr_watchdog */ - 0x9800f8df, +/* 0x00d5: wait_done */ + 0xfc80fcdf, +/* 0x00db: intr_watchdog */ + 0x9800f890, 0x96b003e9, 0x2a0bf400, 0xbb9a0a98, 0x1cf4029a, 0x01d7f00f, - 0x025421f5, + 0x02dd21f5, 0x0ef494bd, -/* 0x00e9: intr_watchdog_next_time */ +/* 0x00f9: intr_watchdog_next_time */ 0x9b0a9815, 0xf400a6b0, 0x9ab8090b, 0x061cf406, -/* 0x00f8: intr_watchdog_next_time_set */ -/* 0x00fb: intr_watchdog_next_proc */ +/* 0x0108: intr_watchdog_next_time_set */ +/* 0x010b: intr_watchdog_next_proc */ 0x809b0980, 0xe0b603e9, 0x68e6b158, 0xc61bf402, -/* 0x010a: intr */ +/* 0x011a: intr */ 0x00f900f8, 0x80f904bd, 0xa0f990f9, @@ -948,13 +954,13 @@ uint32_t nva3_pwr_code[] = { 0xf40289c4, 0x0080230b, 0x58e7f09b, - 0x98cb21f4, + 0x98db21f4, 0x96b09b09, 0x110bf400, 0xb63407f0, 0x09d00604, 0x8004bd00, -/* 0x016e: intr_skip_watchdog */ +/* 0x017e: intr_skip_watchdog */ 0x89e49a09, 0x0bf40800, 0x8897f148, @@ -967,22 +973,22 @@ uint32_t nva3_pwr_code[] = { 0x48e7f1c0, 0x53e3f14f, 0x00d7f054, - 0x02b921f5, + 0x034221f5, 0x07f1c0fc, 0x04b604c0, 0x000cd006, -/* 0x01ae: intr_subintr_skip_fifo */ +/* 0x01be: intr_subintr_skip_fifo */ 0x07f104bd, 0x04b60688, 0x0009d006, -/* 0x01ba: intr_skip_subintr */ +/* 0x01ca: intr_skip_subintr */ 0x89c404bd, 0x070bf420, 0xffbfa4f1, -/* 0x01c4: intr_skip_pause */ +/* 0x01d4: intr_skip_pause */ 0xf44089c4, 0xa4f1070b, -/* 0x01ce: intr_skip_user0 */ +/* 0x01de: intr_skip_user0 */ 0x07f0ffbf, 0x0604b604, 0xbd0008d0, @@ -993,596 +999,732 @@ uint32_t nva3_pwr_code[] = { 0x90fca0fc, 0x00fc80fc, 0xf80032f4, -/* 0x01f5: timer */ - 0x1032f401, - 0xb003f898, - 0x1cf40086, - 0x03fe8051, +/* 0x0205: ticks_from_ns */ + 0xf9c0f901, + 0xcbd7f1b0, + 0x00d3f000, + 0x041321f5, + 0x03e8ccec, + 0xf400b4b0, + 0xeeec120b, + 0xd7f103e8, + 0xd3f000cb, + 0x1321f500, +/* 0x022d: ticks_from_ns_quit */ + 0x02ceb904, + 0xc0fcb0fc, +/* 0x0236: ticks_from_us */ + 0xc0f900f8, + 0xd7f1b0f9, + 0xd3f000cb, + 0x1321f500, + 0x02ceb904, + 0xf400b4b0, + 0xe4bd050b, +/* 0x0250: ticks_from_us_quit */ + 0xc0fcb0fc, +/* 0x0256: ticks_to_us */ + 0xd7f100f8, + 0xd3f000cb, + 0xecedff00, +/* 0x0262: timer */ + 0x90f900f8, + 0x32f480f9, + 0x03f89810, + 0xf40086b0, + 0x84bd651c, 0xb63807f0, 0x08d00604, 0xf004bd00, - 0x84b60887, + 0x84b63487, 0x0088cf06, - 0xf40284f0, - 0x87f0261b, - 0x0684b634, - 0xb80088cf, - 0x0bf406e0, - 0x06e8b809, -/* 0x0233: timer_reset */ - 0xf01f1ef4, - 0x04b63407, - 0x000ed006, - 0x0e8004bd, -/* 0x0241: timer_enable */ - 0x0187f09a, + 0xbb9a0998, + 0xe9bb0298, + 0x03fe8000, + 0xb60887f0, + 0x88cf0684, + 0x0284f000, + 0xf0261bf4, + 0x84b63487, + 0x0088cf06, + 0xf406e0b8, + 0xe8b8090b, + 0x111cf406, +/* 0x02b8: timer_reset */ + 0xb63407f0, + 0x0ed00604, + 0x8004bd00, +/* 0x02c6: timer_enable */ + 0x87f09a0e, + 0x3807f001, + 0xd00604b6, + 0x04bd0008, +/* 0x02d4: timer_done */ + 0xfc1031f4, + 0xf890fc80, +/* 0x02dd: send_proc */ + 0xf980f900, + 0x05e89890, + 0xf004e998, + 0x89b80486, + 0x2a0bf406, + 0x940398c4, + 0x80b60488, + 0x008ebb18, + 0x8000fa98, + 0x8d80008a, + 0x028c8001, + 0xb6038b80, + 0x94f00190, + 0x04e98007, +/* 0x0317: send_done */ + 0xfc0231f4, + 0xf880fc90, +/* 0x031d: find */ + 0xf080f900, + 0x31f45887, +/* 0x0325: find_loop */ + 0x008a9801, + 0xf406aeb8, + 0x80b6100b, + 0x6886b158, + 0xf01bf402, +/* 0x033b: find_done */ + 0xb90132f4, + 0x80fc028e, +/* 0x0342: send */ + 0x21f500f8, + 0x01f4031d, +/* 0x034b: recv */ + 0xf900f897, + 0x9880f990, + 0xe99805e8, + 0x0132f404, + 0xf40689b8, + 0x89c43d0b, + 0x0180b603, + 0x800784f0, + 0xea9805e8, + 0xfef0f902, + 0xf0f9018f, + 0x9402efb9, + 0xe9bb0499, + 0x18e0b600, + 0x9803eb98, + 0xed9802ec, + 0x00ee9801, + 0xf0fca5f9, + 0xf400f8fe, + 0xf0fc0131, +/* 0x0398: recv_done */ + 0x90fc80fc, +/* 0x039e: init */ + 0x17f100f8, + 0x14b60108, + 0x0011cf06, + 0x010911e7, + 0xfe0814b6, + 0x17f10014, + 0x13f000e0, + 0x1c07f000, + 0xd00604b6, + 0x04bd0001, + 0xf0ff17f0, + 0x04b61407, + 0x0001d006, + 0x17f004bd, + 0x0015f102, + 0x1007f008, + 0xd00604b6, + 0x04bd0001, + 0x011a17f1, + 0xfe0013f0, + 0x31f40010, + 0x0117f010, 0xb63807f0, - 0x08d00604, -/* 0x024f: timer_done */ - 0xf404bd00, - 0x00f81031, -/* 0x0254: send_proc */ - 0x90f980f9, - 0x9805e898, - 0x86f004e9, - 0x0689b804, - 0xc42a0bf4, - 0x88940398, - 0x1880b604, - 0x98008ebb, - 0x8a8000fa, - 0x018d8000, - 0x80028c80, - 0x90b6038b, - 0x0794f001, - 0xf404e980, -/* 0x028e: send_done */ - 0x90fc0231, - 0x00f880fc, -/* 0x0294: find */ - 0x87f080f9, - 0x0131f458, -/* 0x029c: find_loop */ - 0xb8008a98, - 0x0bf406ae, - 0x5880b610, - 0x026886b1, - 0xf4f01bf4, -/* 0x02b2: find_done */ - 0x8eb90132, - 0xf880fc02, -/* 0x02b9: send */ - 0x9421f500, - 0x9701f402, -/* 0x02c2: recv */ - 0xe89800f8, - 0x04e99805, - 0xb80132f4, - 0x0bf40689, - 0x0389c43d, - 0xf00180b6, - 0xe8800784, - 0x02ea9805, - 0x8ffef0f9, - 0xb9f0f901, - 0x999402ef, - 0x00e9bb04, - 0x9818e0b6, - 0xec9803eb, - 0x01ed9802, - 0xf900ee98, - 0xfef0fca5, - 0x31f400f8, -/* 0x030b: recv_done */ - 0xf8f0fc01, -/* 0x030d: init */ - 0x0817f100, - 0x0614b601, - 0xe70011cf, - 0xb6010911, - 0x14fe0814, - 0xe017f100, - 0x0013f000, - 0xb61c07f0, 0x01d00604, 0xf004bd00, - 0x07f0ff17, - 0x0604b614, - 0xbd0001d0, - 0x0217f004, - 0x080015f1, - 0xb61007f0, - 0x01d00604, - 0xf104bd00, - 0xf0010a17, - 0x10fe0013, - 0x1031f400, - 0xf00117f0, - 0x04b63807, - 0x0001d006, - 0xf7f004bd, -/* 0x0371: init_proc */ - 0x01f19858, - 0xf40016b0, - 0x15f9fa0b, - 0xf458f0b6, -/* 0x0382: host_send */ - 0x17f1f20e, - 0x14b604b0, - 0x0011cf06, - 0x04a027f1, - 0xcf0624b6, - 0x12b80022, - 0x320bf406, - 0x94071ec4, - 0xe0b704ee, - 0xeb980270, - 0x02ec9803, - 0x9801ed98, - 0x21f500ee, - 0x10b602b9, - 0x0f1ec401, - 0x04b007f1, - 0xd00604b6, - 0x04bd000e, -/* 0x03cb: host_send_done */ - 0xf8ba0ef4, -/* 0x03cd: host_recv */ - 0x4917f100, - 0x5413f14e, - 0x06e1b852, -/* 0x03db: host_recv_wait */ - 0xf1aa0bf4, - 0xb604cc17, - 0x11cf0614, - 0xc827f100, - 0x0624b604, - 0xf00022cf, - 0x12b80816, - 0xe60bf406, - 0xb60723c4, - 0x30b70434, - 0x3b8002f0, - 0x023c8003, - 0x80013d80, - 0x20b6003e, - 0x0f24f001, - 0x04c807f1, +/* 0x0402: init_proc */ + 0xf19858f7, + 0x0016b001, + 0xf9fa0bf4, + 0x58f0b615, +/* 0x0413: mulu32_32_64 */ + 0xf9f20ef4, + 0xf920f910, + 0x9540f930, + 0xd29510e1, + 0xbdc4bd10, + 0xc0edffb4, + 0xb9301dff, + 0x34f10234, + 0x34b6ffff, + 0x1045b610, + 0xbb00c3bb, + 0xe2ff01b4, + 0x0234b930, + 0xffff34f1, + 0xb61034b6, + 0xc3bb1045, + 0x01b4bb00, + 0xbb3012ff, + 0x40fc00b3, + 0x20fc30fc, + 0x00f810fc, +/* 0x0464: host_send */ + 0x04b017f1, + 0xcf0614b6, + 0x27f10011, + 0x24b604a0, + 0x0022cf06, + 0xf40612b8, + 0x1ec4320b, + 0x04ee9407, + 0x0270e0b7, + 0x9803eb98, + 0xed9802ec, + 0x00ee9801, + 0x034221f5, + 0xc40110b6, + 0x07f10f1e, + 0x04b604b0, + 0x000ed006, + 0x0ef404bd, +/* 0x04ad: host_send_done */ +/* 0x04af: host_recv */ + 0xf100f8ba, + 0xf14e4917, + 0xb8525413, + 0x0bf406e1, +/* 0x04bd: host_recv_wait */ + 0xcc17f1aa, + 0x0614b604, + 0xf10011cf, + 0xb604c827, + 0x22cf0624, + 0x0816f000, + 0xf40612b8, + 0x23c4e60b, + 0x0434b607, + 0x02f030b7, + 0x80033b80, + 0x3d80023c, + 0x003e8001, + 0xf00120b6, + 0x07f10f24, + 0x04b604c8, + 0x0002d006, + 0x27f004bd, + 0x0007f040, 0xd00604b6, 0x04bd0002, - 0xf04027f0, - 0x04b60007, - 0x0002d006, - 0x00f804bd, -/* 0x0430: host_init */ - 0x008017f1, - 0xf11014b6, - 0xf1027015, - 0xb604d007, - 0x01d00604, - 0xf104bd00, - 0xb6008017, - 0x15f11014, - 0x07f102f0, - 0x04b604dc, - 0x0001d006, - 0x17f004bd, - 0xc407f101, +/* 0x0512: host_init */ + 0x17f100f8, + 0x14b60080, + 0x7015f110, + 0xd007f102, 0x0604b604, 0xbd0001d0, -/* 0x046f: memx_func_enter */ - 0xf000f804, + 0x8017f104, + 0x1014b600, + 0x02f015f1, + 0x04dc07f1, + 0xd00604b6, + 0x04bd0001, + 0xf10117f0, + 0xb604c407, + 0x01d00604, + 0xf804bd00, +/* 0x0551: memx_func_enter */ + 0x1087f100, + 0x028eb916, + 0xb90421f4, + 0x67f102d7, + 0x63f1fffc, + 0x76fdffff, + 0x0267f104, + 0x0576fd00, + 0x70f980f9, + 0xe0fcd0fc, + 0xf03f21f4, 0x07f10467, 0x04b607e0, 0x0006d006, -/* 0x047e: memx_func_enter_wait */ +/* 0x058a: memx_func_enter_wait */ 0x67f104bd, 0x64b607c0, 0x0066cf06, 0xf40464f0, - 0x1698f30b, - 0x0410b600, -/* 0x0496: memx_func_leave */ - 0x67f000f8, - 0xe407f104, - 0x0604b607, - 0xbd0006d0, -/* 0x04a5: memx_func_leave_wait */ - 0xc067f104, + 0x67f0f30b, + 0x0664b62c, + 0x800066cf, + 0x00f8ee06, +/* 0x05a8: memx_func_leave */ + 0xb62c67f0, + 0x66cf0664, + 0xef068000, + 0xf10467f0, + 0xb607e407, + 0x06d00604, +/* 0x05c3: memx_func_leave_wait */ + 0xf104bd00, + 0xb607c067, + 0x66cf0664, + 0x0464f000, + 0xf1f31bf4, + 0xb9161087, + 0x21f4028e, + 0x02d7b904, + 0xffcc67f1, + 0xffff63f1, + 0xf90476fd, + 0xfc70f980, + 0xf4e0fcd0, + 0x00f83f21, +/* 0x05f8: memx_func_wait_vblank */ + 0xb0001698, + 0x0bf40066, + 0x0166b013, + 0xf4060bf4, +/* 0x060a: memx_func_wait_vblank_head1 */ + 0x77f12e0e, + 0x0ef40020, +/* 0x0611: memx_func_wait_vblank_head0 */ + 0x0877f107, +/* 0x0615: memx_func_wait_vblank_0 */ + 0xc467f100, 0x0664b607, - 0xf00066cf, - 0x1bf40464, -/* 0x04b7: memx_func_wr32 */ - 0x9800f8f3, - 0x15980016, - 0x0810b601, - 0x50f960f9, - 0xe0fcd0fc, - 0xb63f21f4, - 0x1bf40242, -/* 0x04d3: memx_func_wait */ - 0xf000f8e9, - 0x84b62c87, - 0x0088cf06, - 0x98001e98, - 0x1c98011d, - 0x031b9802, - 0xf41010b6, - 0x00f89c21, -/* 0x04f0: memx_func_delay */ - 0xb6001e98, - 0x21f40410, -/* 0x04fb: memx_exec */ - 0xf900f87f, - 0xb9d0f9e0, - 0xb2b902c1, -/* 0x0505: memx_exec_next */ - 0x00139802, - 0x950410b6, - 0x30f01034, - 0xde35980c, - 0x12b855f9, - 0xec1ef406, - 0xe0fcd0fc, - 0x02b921f5, -/* 0x0526: memx_info */ - 0xc7f100f8, - 0xb7f103ac, - 0x21f50800, - 0x00f802b9, -/* 0x0534: memx_recv */ - 0xf401d6b0, - 0xd6b0c40b, - 0xe90bf400, -/* 0x0542: memx_init */ - 0x00f800f8, -/* 0x0544: perf_recv */ -/* 0x0546: perf_init */ + 0xfd0066cf, + 0x1bf40467, +/* 0x0625: memx_func_wait_vblank_1 */ + 0xc467f1f3, + 0x0664b607, + 0xfd0066cf, + 0x0bf40467, +/* 0x0635: memx_func_wait_vblank_fini */ + 0x0410b6f3, +/* 0x063a: memx_func_wr32 */ + 0x169800f8, + 0x01159800, + 0xf90810b6, + 0xfc50f960, + 0xf4e0fcd0, + 0x42b63f21, + 0xe91bf402, +/* 0x0656: memx_func_wait */ + 0x87f000f8, + 0x0684b62c, + 0x980088cf, + 0x1d98001e, + 0x021c9801, + 0xb6031b98, + 0x21f41010, +/* 0x0673: memx_func_delay */ + 0x9800f8a4, + 0x10b6001e, + 0x7f21f404, +/* 0x067e: memx_exec */ + 0xe0f900f8, + 0xc1b9d0f9, + 0x02b2b902, +/* 0x0688: memx_exec_next */ + 0xb6001398, + 0x34e70410, + 0x33e701f0, + 0x32b601e0, + 0x0c30f001, + 0xf9de3598, + 0x0612b855, + 0x98e41ef4, + 0x0c98ee0b, + 0x02cbbbef, + 0x07c4b7f1, + 0xcf06b4b6, + 0xd0fc00bb, + 0x21f5e0fc, + 0x00f80342, +/* 0x06c4: memx_info */ + 0x03c0c7f1, + 0x0800b7f1, + 0x034221f5, +/* 0x06d2: memx_recv */ + 0xd6b000f8, + 0xa90bf401, + 0xf400d6b0, + 0x00f8e90b, +/* 0x06e0: memx_init */ +/* 0x06e2: perf_recv */ 0x00f800f8, -/* 0x0548: i2c_drive_scl */ - 0xf40036b0, - 0x07f1110b, - 0x04b607e0, - 0x0001d006, - 0x00f804bd, -/* 0x055c: i2c_drive_scl_lo */ - 0x07e407f1, - 0xd00604b6, - 0x04bd0001, -/* 0x056a: i2c_drive_sda */ +/* 0x06e4: perf_init */ +/* 0x06e6: i2c_drive_scl */ 0x36b000f8, 0x110bf400, 0x07e007f1, 0xd00604b6, - 0x04bd0002, -/* 0x057e: i2c_drive_sda_lo */ + 0x04bd0001, +/* 0x06fa: i2c_drive_scl_lo */ 0x07f100f8, 0x04b607e4, + 0x0001d006, + 0x00f804bd, +/* 0x0708: i2c_drive_sda */ + 0xf40036b0, + 0x07f1110b, + 0x04b607e0, 0x0002d006, 0x00f804bd, -/* 0x058c: i2c_sense_scl */ - 0xf10132f4, - 0xb607c437, - 0x33cf0634, - 0x0431fd00, - 0xf4060bf4, -/* 0x05a2: i2c_sense_scl_done */ - 0x00f80131, -/* 0x05a4: i2c_sense_sda */ - 0xf10132f4, - 0xb607c437, - 0x33cf0634, - 0x0432fd00, - 0xf4060bf4, -/* 0x05ba: i2c_sense_sda_done */ - 0x00f80131, -/* 0x05bc: i2c_raise_scl */ - 0x47f140f9, - 0x37f00898, - 0x4821f501, -/* 0x05c9: i2c_raise_scl_wait */ - 0xe8e7f105, - 0x7f21f403, - 0x058c21f5, - 0xb60901f4, - 0x1bf40142, -/* 0x05dd: i2c_raise_scl_done */ - 0xf840fcef, -/* 0x05e1: i2c_start */ - 0x8c21f500, - 0x0d11f405, - 0x05a421f5, - 0xf40611f4, -/* 0x05f2: i2c_start_rep */ - 0x37f0300e, - 0x4821f500, - 0x0137f005, - 0x056a21f5, - 0xb60076bb, - 0x50f90465, - 0xbb046594, - 0x50bd0256, - 0xfc0475fd, - 0xbc21f550, - 0x0464b605, -/* 0x061f: i2c_start_send */ - 0xf01f11f4, +/* 0x071c: i2c_drive_sda_lo */ + 0x07e407f1, + 0xd00604b6, + 0x04bd0002, +/* 0x072a: i2c_sense_scl */ + 0x32f400f8, + 0xc437f101, + 0x0634b607, + 0xfd0033cf, + 0x0bf40431, + 0x0131f406, +/* 0x0740: i2c_sense_scl_done */ +/* 0x0742: i2c_sense_sda */ + 0x32f400f8, + 0xc437f101, + 0x0634b607, + 0xfd0033cf, + 0x0bf40432, + 0x0131f406, +/* 0x0758: i2c_sense_sda_done */ +/* 0x075a: i2c_raise_scl */ + 0x40f900f8, + 0x089847f1, + 0xf50137f0, +/* 0x0767: i2c_raise_scl_wait */ + 0xf106e621, + 0xf403e8e7, + 0x21f57f21, + 0x01f4072a, + 0x0142b609, +/* 0x077b: i2c_raise_scl_done */ + 0xfcef1bf4, +/* 0x077f: i2c_start */ + 0xf500f840, + 0xf4072a21, + 0x21f50d11, + 0x11f40742, + 0x300ef406, +/* 0x0790: i2c_start_rep */ + 0xf50037f0, + 0xf006e621, + 0x21f50137, + 0x76bb0708, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb6075a21, + 0x11f40464, +/* 0x07bd: i2c_start_send */ + 0x0037f01f, + 0x070821f5, + 0x1388e7f1, + 0xf07f21f4, 0x21f50037, - 0xe7f1056a, + 0xe7f106e6, 0x21f41388, - 0x0037f07f, - 0x054821f5, - 0x1388e7f1, -/* 0x063b: i2c_start_out */ - 0xf87f21f4, -/* 0x063d: i2c_stop */ - 0x0037f000, - 0x054821f5, - 0xf50037f0, - 0xf1056a21, - 0xf403e8e7, - 0x37f07f21, - 0x4821f501, - 0x88e7f105, - 0x7f21f413, +/* 0x07d9: i2c_start_out */ +/* 0x07db: i2c_stop */ + 0xf000f87f, + 0x21f50037, + 0x37f006e6, + 0x0821f500, + 0xe8e7f107, + 0x7f21f403, 0xf50137f0, - 0xf1056a21, + 0xf106e621, 0xf41388e7, - 0x00f87f21, -/* 0x0670: i2c_bitw */ - 0x056a21f5, - 0x03e8e7f1, - 0xbb7f21f4, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x05bc21f5, - 0xf40464b6, - 0xe7f11811, - 0x21f41388, - 0x0037f07f, - 0x054821f5, - 0x1388e7f1, -/* 0x06af: i2c_bitw_out */ - 0xf87f21f4, -/* 0x06b1: i2c_bitr */ - 0x0137f000, - 0x056a21f5, - 0x03e8e7f1, - 0xbb7f21f4, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x05bc21f5, - 0xf40464b6, - 0x21f51b11, - 0x37f005a4, - 0x4821f500, - 0x88e7f105, + 0x37f07f21, + 0x0821f501, + 0x88e7f107, 0x7f21f413, - 0xf4013cf0, -/* 0x06f6: i2c_bitr_done */ - 0x00f80131, -/* 0x06f8: i2c_get_byte */ - 0xf00057f0, -/* 0x06fe: i2c_get_byte_next */ - 0x54b60847, - 0x0076bb01, +/* 0x080e: i2c_bitw */ + 0x21f500f8, + 0xe7f10708, + 0x21f403e8, + 0x0076bb7f, 0xf90465b6, 0x04659450, 0xbd0256bb, 0x0475fd50, 0x21f550fc, - 0x64b606b1, - 0x2b11f404, - 0xb60553fd, - 0x1bf40142, - 0x0137f0d8, - 0xb60076bb, - 0x50f90465, - 0xbb046594, - 0x50bd0256, - 0xfc0475fd, - 0x7021f550, - 0x0464b606, -/* 0x0748: i2c_get_byte_done */ -/* 0x074a: i2c_put_byte */ - 0x47f000f8, -/* 0x074d: i2c_put_byte_next */ - 0x0142b608, - 0xbb3854ff, + 0x64b6075a, + 0x1811f404, + 0x1388e7f1, + 0xf07f21f4, + 0x21f50037, + 0xe7f106e6, + 0x21f41388, +/* 0x084d: i2c_bitw_out */ +/* 0x084f: i2c_bitr */ + 0xf000f87f, + 0x21f50137, + 0xe7f10708, + 0x21f403e8, + 0x0076bb7f, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b6075a, + 0x1b11f404, + 0x074221f5, + 0xf50037f0, + 0xf106e621, + 0xf41388e7, + 0x3cf07f21, + 0x0131f401, +/* 0x0894: i2c_bitr_done */ +/* 0x0896: i2c_get_byte */ + 0x57f000f8, + 0x0847f000, +/* 0x089c: i2c_get_byte_next */ + 0xbb0154b6, 0x65b60076, 0x9450f904, 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x067021f5, + 0x084f21f5, 0xf40464b6, - 0x46b03411, - 0xd81bf400, - 0xb60076bb, - 0x50f90465, - 0xbb046594, - 0x50bd0256, - 0xfc0475fd, - 0xb121f550, - 0x0464b606, - 0xbb0f11f4, - 0x36b00076, - 0x061bf401, -/* 0x07a3: i2c_put_byte_done */ - 0xf80132f4, -/* 0x07a5: i2c_addr */ - 0x0076bb00, + 0x53fd2b11, + 0x0142b605, + 0xf0d81bf4, + 0x76bb0137, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb6080e21, +/* 0x08e6: i2c_get_byte_done */ + 0x00f80464, +/* 0x08e8: i2c_put_byte */ +/* 0x08eb: i2c_put_byte_next */ + 0xb60847f0, + 0x54ff0142, + 0x0076bb38, 0xf90465b6, 0x04659450, 0xbd0256bb, 0x0475fd50, 0x21f550fc, - 0x64b605e1, - 0x2911f404, - 0x012ec3e7, - 0xfd0134b6, - 0x76bb0553, + 0x64b6080e, + 0x3411f404, + 0xf40046b0, + 0x76bbd81b, 0x0465b600, 0x659450f9, 0x0256bb04, 0x75fd50bd, 0xf550fc04, - 0xb6074a21, -/* 0x07ea: i2c_addr_done */ - 0x00f80464, -/* 0x07ec: i2c_acquire_addr */ - 0xb6f8cec7, - 0xe0b702e4, - 0xee980bfc, -/* 0x07fb: i2c_acquire */ - 0xf500f800, - 0xf407ec21, - 0xd9f00421, - 0x3f21f403, -/* 0x080a: i2c_release */ - 0x21f500f8, - 0x21f407ec, - 0x03daf004, - 0xf83f21f4, -/* 0x0819: i2c_recv */ - 0x0132f400, - 0xb6f8c1c7, - 0x16b00214, - 0x3a1ff528, - 0xd413a001, - 0x0032980b, - 0x0bac13a0, - 0xf4003198, - 0xd0f90231, - 0xd0f9e0f9, - 0x000067f1, - 0x100063f1, - 0xbb016792, + 0xb6084f21, + 0x11f40464, + 0x0076bb0f, + 0xf40136b0, + 0x32f4061b, +/* 0x0941: i2c_put_byte_done */ +/* 0x0943: i2c_addr */ + 0xbb00f801, 0x65b60076, 0x9450f904, 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x07fb21f5, - 0xfc0464b6, - 0x00d6b0d0, - 0x00b31bf5, - 0xbb0057f0, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x07a521f5, - 0xf50464b6, - 0xc700d011, - 0x76bbe0c5, - 0x0465b600, - 0x659450f9, - 0x0256bb04, - 0x75fd50bd, - 0xf550fc04, - 0xb6074a21, - 0x11f50464, - 0x57f000ad, + 0x077f21f5, + 0xf40464b6, + 0xc3e72911, + 0x34b6012e, + 0x0553fd01, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0xe821f550, + 0x0464b608, +/* 0x0988: i2c_addr_done */ +/* 0x098a: i2c_acquire_addr */ + 0xcec700f8, + 0x02e4b6f8, + 0x0c10e0b7, + 0xf800ee98, +/* 0x0999: i2c_acquire */ + 0x8a21f500, + 0x0421f409, + 0xf403d9f0, + 0x00f83f21, +/* 0x09a8: i2c_release */ + 0x098a21f5, + 0xf00421f4, + 0x21f403da, +/* 0x09b7: i2c_recv */ + 0xf400f83f, + 0xc1c70132, + 0x0214b6f8, + 0xf52816b0, + 0xa0013a1f, + 0x980be813, + 0x13a00032, + 0x31980bc0, + 0x0231f400, + 0xe0f9d0f9, + 0x67f1d0f9, + 0x63f10000, + 0x67921000, 0x0076bb01, 0xf90465b6, 0x04659450, 0xbd0256bb, 0x0475fd50, 0x21f550fc, - 0x64b607a5, - 0x8a11f504, + 0x64b60999, + 0xb0d0fc04, + 0x1bf500d6, + 0x57f000b3, 0x0076bb00, 0xf90465b6, 0x04659450, 0xbd0256bb, 0x0475fd50, 0x21f550fc, - 0x64b606f8, - 0x6a11f404, - 0xbbe05bcb, + 0x64b60943, + 0xd011f504, + 0xe0c5c700, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0xe821f550, + 0x0464b608, + 0x00ad11f5, + 0xbb0157f0, 0x65b60076, 0x9450f904, 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x063d21f5, - 0xb90464b6, - 0x74bd025b, -/* 0x091f: i2c_recv_not_rd08 */ - 0xb0430ef4, - 0x1bf401d6, - 0x0057f03d, - 0x07a521f5, - 0xc73311f4, - 0x21f5e0c5, - 0x11f4074a, - 0x0057f029, - 0x07a521f5, - 0xc71f11f4, - 0x21f5e0b5, - 0x11f4074a, - 0x3d21f515, - 0xc774bd06, - 0x1bf408c5, - 0x0232f409, -/* 0x095f: i2c_recv_not_wr08 */ -/* 0x095f: i2c_recv_done */ - 0xc7030ef4, - 0x21f5f8ce, - 0xe0fc080a, - 0x12f4d0fc, - 0x027cb90a, - 0x02b921f5, -/* 0x0974: i2c_recv_exit */ -/* 0x0976: i2c_init */ - 0x00f800f8, -/* 0x0978: test_recv */ - 0x05d817f1, + 0x094321f5, + 0xf50464b6, + 0xbb008a11, + 0x65b60076, + 0x9450f904, + 0x56bb0465, + 0xfd50bd02, + 0x50fc0475, + 0x089621f5, + 0xf40464b6, + 0x5bcb6a11, + 0x0076bbe0, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b607db, + 0x025bb904, + 0x0ef474bd, +/* 0x0abd: i2c_recv_not_rd08 */ + 0x01d6b043, + 0xf03d1bf4, + 0x21f50057, + 0x11f40943, + 0xe0c5c733, + 0x08e821f5, + 0xf02911f4, + 0x21f50057, + 0x11f40943, + 0xe0b5c71f, + 0x08e821f5, + 0xf51511f4, + 0xbd07db21, + 0x08c5c774, + 0xf4091bf4, + 0x0ef40232, +/* 0x0afd: i2c_recv_not_wr08 */ +/* 0x0afd: i2c_recv_done */ + 0xf8cec703, + 0x09a821f5, + 0xd0fce0fc, + 0xb90a12f4, + 0x21f5027c, +/* 0x0b12: i2c_recv_exit */ + 0x00f80342, +/* 0x0b14: i2c_init */ +/* 0x0b16: test_recv */ + 0x17f100f8, + 0x14b605d8, + 0x0011cf06, + 0xf10110b6, + 0xb605d807, + 0x01d00604, + 0xf104bd00, + 0xf1d900e7, + 0xf5134fe3, + 0xf8026221, +/* 0x0b3d: test_init */ + 0x00e7f100, + 0x6221f508, +/* 0x0b47: idle_recv */ + 0xf800f802, +/* 0x0b49: idle */ + 0x0031f400, + 0x05d417f1, 0xcf0614b6, 0x10b60011, - 0xd807f101, + 0xd407f101, 0x0604b605, 0xbd0001d0, - 0x00e7f104, - 0x4fe3f1d9, - 0xf521f513, -/* 0x099f: test_init */ - 0xf100f801, - 0xf50800e7, - 0xf801f521, -/* 0x09a9: idle_recv */ -/* 0x09ab: idle */ - 0xf400f800, - 0x17f10031, - 0x14b605d4, - 0x0011cf06, - 0xf10110b6, - 0xb605d407, - 0x01d00604, -/* 0x09c7: idle_loop */ - 0xf004bd00, - 0x32f45817, -/* 0x09cd: idle_proc */ -/* 0x09cd: idle_proc_exec */ - 0xb910f902, - 0x21f5021e, - 0x10fc02c2, - 0xf40911f4, - 0x0ef40231, -/* 0x09e1: idle_proc_next */ - 0x5810b6ef, - 0xf4061fb8, - 0x02f4e61b, - 0x0028f4dd, - 0x00bb0ef4, +/* 0x0b65: idle_loop */ + 0x5817f004, +/* 0x0b6b: idle_proc */ +/* 0x0b6b: idle_proc_exec */ + 0xf90232f4, + 0x021eb910, + 0x034b21f5, + 0x11f410fc, + 0x0231f409, +/* 0x0b7f: idle_proc_next */ + 0xb6ef0ef4, + 0x1fb85810, + 0xe61bf406, + 0xf4dd02f4, + 0x0ef40028, + 0x000000bb, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc index 48f79434a4491f30c61f95755d43c37097f73249..21bf8cc7618f8459ed43e1699ee28278400a4ce5 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc @@ -23,6 +23,7 @@ */ #define NVKM_PPWR_CHIPSET GF100 +#define HW_TICKS_PER_US 203 // should be 202.5 //#define NVKM_FALCON_PC24 //#define NVKM_FALCON_UNSHIFTED_IO @@ -34,6 +35,7 @@ .section #nvc0_pwr_data #define INCLUDE_PROC #include "kernel.fuc" +#include "arith.fuc" #include "host.fuc" #include "memx.fuc" #include "perf.fuc" @@ -44,6 +46,7 @@ #define INCLUDE_DATA #include "kernel.fuc" +#include "arith.fuc" #include "host.fuc" #include "memx.fuc" #include "perf.fuc" @@ -56,6 +59,7 @@ .section #nvc0_pwr_code #define INCLUDE_CODE #include "kernel.fuc" +#include "arith.fuc" #include "host.fuc" #include "memx.fuc" #include "perf.fuc" diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc.h index 0773ff0e3dc34b1cd1a22dc2381f0dac4ed7e0d6..ca30fa4011b55907bb1f5d7b207b3291689f55e2 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc.h @@ -24,8 +24,8 @@ uint32_t nvc0_pwr_data[] = { 0x00000000, /* 0x0058: proc_list_head */ 0x54534f48, - 0x00000430, - 0x000003cd, + 0x00000512, + 0x000004af, 0x00000000, 0x00000000, 0x00000000, @@ -46,8 +46,8 @@ uint32_t nvc0_pwr_data[] = { 0x00000000, 0x00000000, 0x584d454d, - 0x00000542, - 0x00000534, + 0x0000074b, + 0x0000073d, 0x00000000, 0x00000000, 0x00000000, @@ -68,8 +68,8 @@ uint32_t nvc0_pwr_data[] = { 0x00000000, 0x00000000, 0x46524550, - 0x00000546, - 0x00000544, + 0x0000074f, + 0x0000074d, 0x00000000, 0x00000000, 0x00000000, @@ -90,8 +90,8 @@ uint32_t nvc0_pwr_data[] = { 0x00000000, 0x00000000, 0x5f433249, - 0x00000976, - 0x00000819, + 0x00000b7f, + 0x00000a22, 0x00000000, 0x00000000, 0x00000000, @@ -112,8 +112,8 @@ uint32_t nvc0_pwr_data[] = { 0x00000000, 0x00000000, 0x54534554, - 0x0000099f, - 0x00000978, + 0x00000ba8, + 0x00000b81, 0x00000000, 0x00000000, 0x00000000, @@ -134,8 +134,8 @@ uint32_t nvc0_pwr_data[] = { 0x00000000, 0x00000000, 0x454c4449, - 0x000009ab, - 0x000009a9, + 0x00000bb4, + 0x00000bb2, 0x00000000, 0x00000000, 0x00000000, @@ -227,25 +227,31 @@ uint32_t nvc0_pwr_data[] = { 0x00000000, 0x00000000, /* 0x0370: memx_func_head */ - 0x00010000, - 0x00000000, - 0x0000046f, -/* 0x037c: memx_func_next */ 0x00000001, 0x00000000, - 0x00000496, + 0x00000551, +/* 0x037c: memx_func_next */ 0x00000002, + 0x00000000, + 0x000005db, + 0x00000003, 0x00000002, - 0x000004b7, - 0x00040003, + 0x000006a5, + 0x00040004, + 0x00000000, + 0x000006c1, + 0x00010005, + 0x00000000, + 0x000006de, + 0x00010006, 0x00000000, - 0x000004d3, - 0x00010004, + 0x00000663, +/* 0x03b8: memx_func_tail */ +/* 0x03b8: memx_ts_start */ 0x00000000, - 0x000004f0, -/* 0x03ac: memx_func_tail */ -/* 0x03ac: memx_data_head */ +/* 0x03bc: memx_ts_end */ 0x00000000, +/* 0x03c0: memx_data_head */ 0x00000000, 0x00000000, 0x00000000, @@ -757,8 +763,9 @@ uint32_t nvc0_pwr_data[] = { 0x00000000, 0x00000000, 0x00000000, -/* 0x0bac: memx_data_tail */ -/* 0x0bac: i2c_scl_map */ + 0x00000000, +/* 0x0bc0: memx_data_tail */ +/* 0x0bc0: i2c_scl_map */ 0x00001000, 0x00004000, 0x00010000, @@ -769,7 +776,7 @@ uint32_t nvc0_pwr_data[] = { 0x01000000, 0x04000000, 0x10000000, -/* 0x0bd4: i2c_sda_map */ +/* 0x0be8: i2c_sda_map */ 0x00002000, 0x00008000, 0x00020000, @@ -780,7 +787,7 @@ uint32_t nvc0_pwr_data[] = { 0x02000000, 0x08000000, 0x20000000, -/* 0x0bfc: i2c_ctrl */ +/* 0x0c10: i2c_ctrl */ 0x0000e138, 0x0000e150, 0x0000e168, @@ -841,15 +848,10 @@ uint32_t nvc0_pwr_data[] = { 0x00000000, 0x00000000, 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, }; uint32_t nvc0_pwr_code[] = { - 0x030d0ef5, + 0x039e0ef5, /* 0x0004: rd32 */ 0x07a007f1, 0xd00604b6, @@ -885,19 +887,22 @@ uint32_t nvc0_pwr_code[] = { 0xd4f100dd, 0x1bf47000, /* 0x007f: nsec */ - 0xf000f8f2, + 0xf900f8f2, + 0xf080f990, 0x84b62c87, 0x0088cf06, -/* 0x0088: nsec_loop */ +/* 0x008c: nsec_loop */ 0xb62c97f0, 0x99cf0694, 0x0298bb00, 0xf4069eb8, - 0x00f8f11e, -/* 0x009c: wait */ + 0x80fcf11e, + 0x00f890fc, +/* 0x00a4: wait */ + 0x80f990f9, 0xb62c87f0, 0x88cf0684, -/* 0x00a5: wait_loop */ +/* 0x00b1: wait_loop */ 0x02eeb900, 0xb90421f4, 0xadfd02da, @@ -907,28 +912,29 @@ uint32_t nvc0_pwr_code[] = { 0x0099cf06, 0xb80298bb, 0x1ef4069b, -/* 0x00c9: wait_done */ -/* 0x00cb: intr_watchdog */ - 0x9800f8df, +/* 0x00d5: wait_done */ + 0xfc80fcdf, +/* 0x00db: intr_watchdog */ + 0x9800f890, 0x96b003e9, 0x2a0bf400, 0xbb9a0a98, 0x1cf4029a, 0x01d7f00f, - 0x025421f5, + 0x02dd21f5, 0x0ef494bd, -/* 0x00e9: intr_watchdog_next_time */ +/* 0x00f9: intr_watchdog_next_time */ 0x9b0a9815, 0xf400a6b0, 0x9ab8090b, 0x061cf406, -/* 0x00f8: intr_watchdog_next_time_set */ -/* 0x00fb: intr_watchdog_next_proc */ +/* 0x0108: intr_watchdog_next_time_set */ +/* 0x010b: intr_watchdog_next_proc */ 0x809b0980, 0xe0b603e9, 0x68e6b158, 0xc61bf402, -/* 0x010a: intr */ +/* 0x011a: intr */ 0x00f900f8, 0x80f904bd, 0xa0f990f9, @@ -948,13 +954,13 @@ uint32_t nvc0_pwr_code[] = { 0xf40289c4, 0x0080230b, 0x58e7f09b, - 0x98cb21f4, + 0x98db21f4, 0x96b09b09, 0x110bf400, 0xb63407f0, 0x09d00604, 0x8004bd00, -/* 0x016e: intr_skip_watchdog */ +/* 0x017e: intr_skip_watchdog */ 0x89e49a09, 0x0bf40800, 0x8897f148, @@ -967,22 +973,22 @@ uint32_t nvc0_pwr_code[] = { 0x48e7f1c0, 0x53e3f14f, 0x00d7f054, - 0x02b921f5, + 0x034221f5, 0x07f1c0fc, 0x04b604c0, 0x000cd006, -/* 0x01ae: intr_subintr_skip_fifo */ +/* 0x01be: intr_subintr_skip_fifo */ 0x07f104bd, 0x04b60688, 0x0009d006, -/* 0x01ba: intr_skip_subintr */ +/* 0x01ca: intr_skip_subintr */ 0x89c404bd, 0x070bf420, 0xffbfa4f1, -/* 0x01c4: intr_skip_pause */ +/* 0x01d4: intr_skip_pause */ 0xf44089c4, 0xa4f1070b, -/* 0x01ce: intr_skip_user0 */ +/* 0x01de: intr_skip_user0 */ 0x07f0ffbf, 0x0604b604, 0xbd0008d0, @@ -993,597 +999,733 @@ uint32_t nvc0_pwr_code[] = { 0x90fca0fc, 0x00fc80fc, 0xf80032f4, -/* 0x01f5: timer */ - 0x1032f401, - 0xb003f898, - 0x1cf40086, - 0x03fe8051, +/* 0x0205: ticks_from_ns */ + 0xf9c0f901, + 0xcbd7f1b0, + 0x00d3f000, + 0x041321f5, + 0x03e8ccec, + 0xf400b4b0, + 0xeeec120b, + 0xd7f103e8, + 0xd3f000cb, + 0x1321f500, +/* 0x022d: ticks_from_ns_quit */ + 0x02ceb904, + 0xc0fcb0fc, +/* 0x0236: ticks_from_us */ + 0xc0f900f8, + 0xd7f1b0f9, + 0xd3f000cb, + 0x1321f500, + 0x02ceb904, + 0xf400b4b0, + 0xe4bd050b, +/* 0x0250: ticks_from_us_quit */ + 0xc0fcb0fc, +/* 0x0256: ticks_to_us */ + 0xd7f100f8, + 0xd3f000cb, + 0xecedff00, +/* 0x0262: timer */ + 0x90f900f8, + 0x32f480f9, + 0x03f89810, + 0xf40086b0, + 0x84bd651c, 0xb63807f0, 0x08d00604, 0xf004bd00, - 0x84b60887, + 0x84b63487, 0x0088cf06, - 0xf40284f0, - 0x87f0261b, - 0x0684b634, - 0xb80088cf, - 0x0bf406e0, - 0x06e8b809, -/* 0x0233: timer_reset */ - 0xf01f1ef4, - 0x04b63407, - 0x000ed006, - 0x0e8004bd, -/* 0x0241: timer_enable */ - 0x0187f09a, + 0xbb9a0998, + 0xe9bb0298, + 0x03fe8000, + 0xb60887f0, + 0x88cf0684, + 0x0284f000, + 0xf0261bf4, + 0x84b63487, + 0x0088cf06, + 0xf406e0b8, + 0xe8b8090b, + 0x111cf406, +/* 0x02b8: timer_reset */ + 0xb63407f0, + 0x0ed00604, + 0x8004bd00, +/* 0x02c6: timer_enable */ + 0x87f09a0e, + 0x3807f001, + 0xd00604b6, + 0x04bd0008, +/* 0x02d4: timer_done */ + 0xfc1031f4, + 0xf890fc80, +/* 0x02dd: send_proc */ + 0xf980f900, + 0x05e89890, + 0xf004e998, + 0x89b80486, + 0x2a0bf406, + 0x940398c4, + 0x80b60488, + 0x008ebb18, + 0x8000fa98, + 0x8d80008a, + 0x028c8001, + 0xb6038b80, + 0x94f00190, + 0x04e98007, +/* 0x0317: send_done */ + 0xfc0231f4, + 0xf880fc90, +/* 0x031d: find */ + 0xf080f900, + 0x31f45887, +/* 0x0325: find_loop */ + 0x008a9801, + 0xf406aeb8, + 0x80b6100b, + 0x6886b158, + 0xf01bf402, +/* 0x033b: find_done */ + 0xb90132f4, + 0x80fc028e, +/* 0x0342: send */ + 0x21f500f8, + 0x01f4031d, +/* 0x034b: recv */ + 0xf900f897, + 0x9880f990, + 0xe99805e8, + 0x0132f404, + 0xf40689b8, + 0x89c43d0b, + 0x0180b603, + 0x800784f0, + 0xea9805e8, + 0xfef0f902, + 0xf0f9018f, + 0x9402efb9, + 0xe9bb0499, + 0x18e0b600, + 0x9803eb98, + 0xed9802ec, + 0x00ee9801, + 0xf0fca5f9, + 0xf400f8fe, + 0xf0fc0131, +/* 0x0398: recv_done */ + 0x90fc80fc, +/* 0x039e: init */ + 0x17f100f8, + 0x14b60108, + 0x0011cf06, + 0x010911e7, + 0xfe0814b6, + 0x17f10014, + 0x13f000e0, + 0x1c07f000, + 0xd00604b6, + 0x04bd0001, + 0xf0ff17f0, + 0x04b61407, + 0x0001d006, + 0x17f004bd, + 0x0015f102, + 0x1007f008, + 0xd00604b6, + 0x04bd0001, + 0x011a17f1, + 0xfe0013f0, + 0x31f40010, + 0x0117f010, 0xb63807f0, - 0x08d00604, -/* 0x024f: timer_done */ - 0xf404bd00, - 0x00f81031, -/* 0x0254: send_proc */ - 0x90f980f9, - 0x9805e898, - 0x86f004e9, - 0x0689b804, - 0xc42a0bf4, - 0x88940398, - 0x1880b604, - 0x98008ebb, - 0x8a8000fa, - 0x018d8000, - 0x80028c80, - 0x90b6038b, - 0x0794f001, - 0xf404e980, -/* 0x028e: send_done */ - 0x90fc0231, - 0x00f880fc, -/* 0x0294: find */ - 0x87f080f9, - 0x0131f458, -/* 0x029c: find_loop */ - 0xb8008a98, - 0x0bf406ae, - 0x5880b610, - 0x026886b1, - 0xf4f01bf4, -/* 0x02b2: find_done */ - 0x8eb90132, - 0xf880fc02, -/* 0x02b9: send */ - 0x9421f500, - 0x9701f402, -/* 0x02c2: recv */ - 0xe89800f8, - 0x04e99805, - 0xb80132f4, - 0x0bf40689, - 0x0389c43d, - 0xf00180b6, - 0xe8800784, - 0x02ea9805, - 0x8ffef0f9, - 0xb9f0f901, - 0x999402ef, - 0x00e9bb04, - 0x9818e0b6, - 0xec9803eb, - 0x01ed9802, - 0xf900ee98, - 0xfef0fca5, - 0x31f400f8, -/* 0x030b: recv_done */ - 0xf8f0fc01, -/* 0x030d: init */ - 0x0817f100, - 0x0614b601, - 0xe70011cf, - 0xb6010911, - 0x14fe0814, - 0xe017f100, - 0x0013f000, - 0xb61c07f0, 0x01d00604, 0xf004bd00, - 0x07f0ff17, - 0x0604b614, - 0xbd0001d0, - 0x0217f004, - 0x080015f1, - 0xb61007f0, - 0x01d00604, - 0xf104bd00, - 0xf0010a17, - 0x10fe0013, - 0x1031f400, - 0xf00117f0, - 0x04b63807, - 0x0001d006, - 0xf7f004bd, -/* 0x0371: init_proc */ - 0x01f19858, - 0xf40016b0, - 0x15f9fa0b, - 0xf458f0b6, -/* 0x0382: host_send */ - 0x17f1f20e, - 0x14b604b0, - 0x0011cf06, - 0x04a027f1, - 0xcf0624b6, - 0x12b80022, - 0x320bf406, - 0x94071ec4, - 0xe0b704ee, - 0xeb980270, - 0x02ec9803, - 0x9801ed98, - 0x21f500ee, - 0x10b602b9, - 0x0f1ec401, - 0x04b007f1, - 0xd00604b6, - 0x04bd000e, -/* 0x03cb: host_send_done */ - 0xf8ba0ef4, -/* 0x03cd: host_recv */ - 0x4917f100, - 0x5413f14e, - 0x06e1b852, -/* 0x03db: host_recv_wait */ - 0xf1aa0bf4, - 0xb604cc17, - 0x11cf0614, - 0xc827f100, - 0x0624b604, - 0xf00022cf, - 0x12b80816, - 0xe60bf406, - 0xb60723c4, - 0x30b70434, - 0x3b8002f0, - 0x023c8003, - 0x80013d80, - 0x20b6003e, - 0x0f24f001, - 0x04c807f1, +/* 0x0402: init_proc */ + 0xf19858f7, + 0x0016b001, + 0xf9fa0bf4, + 0x58f0b615, +/* 0x0413: mulu32_32_64 */ + 0xf9f20ef4, + 0xf920f910, + 0x9540f930, + 0xd29510e1, + 0xbdc4bd10, + 0xc0edffb4, + 0xb9301dff, + 0x34f10234, + 0x34b6ffff, + 0x1045b610, + 0xbb00c3bb, + 0xe2ff01b4, + 0x0234b930, + 0xffff34f1, + 0xb61034b6, + 0xc3bb1045, + 0x01b4bb00, + 0xbb3012ff, + 0x40fc00b3, + 0x20fc30fc, + 0x00f810fc, +/* 0x0464: host_send */ + 0x04b017f1, + 0xcf0614b6, + 0x27f10011, + 0x24b604a0, + 0x0022cf06, + 0xf40612b8, + 0x1ec4320b, + 0x04ee9407, + 0x0270e0b7, + 0x9803eb98, + 0xed9802ec, + 0x00ee9801, + 0x034221f5, + 0xc40110b6, + 0x07f10f1e, + 0x04b604b0, + 0x000ed006, + 0x0ef404bd, +/* 0x04ad: host_send_done */ +/* 0x04af: host_recv */ + 0xf100f8ba, + 0xf14e4917, + 0xb8525413, + 0x0bf406e1, +/* 0x04bd: host_recv_wait */ + 0xcc17f1aa, + 0x0614b604, + 0xf10011cf, + 0xb604c827, + 0x22cf0624, + 0x0816f000, + 0xf40612b8, + 0x23c4e60b, + 0x0434b607, + 0x02f030b7, + 0x80033b80, + 0x3d80023c, + 0x003e8001, + 0xf00120b6, + 0x07f10f24, + 0x04b604c8, + 0x0002d006, + 0x27f004bd, + 0x0007f040, 0xd00604b6, 0x04bd0002, - 0xf04027f0, - 0x04b60007, - 0x0002d006, - 0x00f804bd, -/* 0x0430: host_init */ - 0x008017f1, - 0xf11014b6, - 0xf1027015, - 0xb604d007, - 0x01d00604, - 0xf104bd00, - 0xb6008017, - 0x15f11014, - 0x07f102f0, - 0x04b604dc, - 0x0001d006, - 0x17f004bd, - 0xc407f101, +/* 0x0512: host_init */ + 0x17f100f8, + 0x14b60080, + 0x7015f110, + 0xd007f102, 0x0604b604, 0xbd0001d0, -/* 0x046f: memx_func_enter */ - 0xf000f804, + 0x8017f104, + 0x1014b600, + 0x02f015f1, + 0x04dc07f1, + 0xd00604b6, + 0x04bd0001, + 0xf10117f0, + 0xb604c407, + 0x01d00604, + 0xf804bd00, +/* 0x0551: memx_func_enter */ + 0x2067f100, + 0x5d77f116, + 0xff73f1f5, + 0x026eb9ff, + 0xb90421f4, + 0x87fd02d8, + 0xf960f904, + 0xfcd0fc80, + 0x3f21f4e0, + 0xfffe77f1, + 0xffff73f1, + 0xf4026eb9, + 0xd8b90421, + 0x0487fd02, + 0x80f960f9, + 0xe0fcd0fc, + 0xf13f21f4, + 0xb926f067, + 0x21f4026e, + 0x02d8b904, + 0xf90487fd, + 0xfc80f960, + 0xf4e0fcd0, + 0x67f03f21, + 0xe007f104, + 0x0604b607, + 0xbd0006d0, +/* 0x05bd: memx_func_enter_wait */ + 0xc067f104, + 0x0664b607, + 0xf00066cf, + 0x0bf40464, + 0x2c67f0f3, + 0xcf0664b6, + 0x06800066, +/* 0x05db: memx_func_leave */ + 0xf000f8ee, + 0x64b62c67, + 0x0066cf06, + 0xf0ef0680, 0x07f10467, - 0x04b607e0, + 0x04b607e4, 0x0006d006, -/* 0x047e: memx_func_enter_wait */ +/* 0x05f6: memx_func_leave_wait */ 0x67f104bd, 0x64b607c0, 0x0066cf06, 0xf40464f0, - 0x1698f30b, + 0x67f1f31b, + 0x77f126f0, + 0x73f00001, + 0x026eb900, + 0xb90421f4, + 0x87fd02d8, + 0xf960f905, + 0xfcd0fc80, + 0x3f21f4e0, + 0x162067f1, + 0xf4026eb9, + 0xd8b90421, + 0x0587fd02, + 0x80f960f9, + 0xe0fcd0fc, + 0xf13f21f4, + 0xf00aa277, + 0x6eb90073, + 0x0421f402, + 0xfd02d8b9, + 0x60f90587, + 0xd0fc80f9, + 0x21f4e0fc, +/* 0x0663: memx_func_wait_vblank */ + 0x9800f83f, + 0x66b00016, + 0x130bf400, + 0xf40166b0, + 0x0ef4060b, +/* 0x0675: memx_func_wait_vblank_head1 */ + 0x2077f12e, + 0x070ef400, +/* 0x067c: memx_func_wait_vblank_head0 */ + 0x000877f1, +/* 0x0680: memx_func_wait_vblank_0 */ + 0x07c467f1, + 0xcf0664b6, + 0x67fd0066, + 0xf31bf404, +/* 0x0690: memx_func_wait_vblank_1 */ + 0x07c467f1, + 0xcf0664b6, + 0x67fd0066, + 0xf30bf404, +/* 0x06a0: memx_func_wait_vblank_fini */ + 0xf80410b6, +/* 0x06a5: memx_func_wr32 */ + 0x00169800, + 0xb6011598, + 0x60f90810, + 0xd0fc50f9, + 0x21f4e0fc, + 0x0242b63f, + 0xf8e91bf4, +/* 0x06c1: memx_func_wait */ + 0x2c87f000, + 0xcf0684b6, + 0x1e980088, + 0x011d9800, + 0x98021c98, + 0x10b6031b, + 0xa421f410, +/* 0x06de: memx_func_delay */ + 0x1e9800f8, 0x0410b600, -/* 0x0496: memx_func_leave */ - 0x67f000f8, - 0xe407f104, + 0xf87f21f4, +/* 0x06e9: memx_exec */ + 0xf9e0f900, + 0x02c1b9d0, +/* 0x06f3: memx_exec_next */ + 0x9802b2b9, + 0x10b60013, + 0xf034e704, + 0xe033e701, + 0x0132b601, + 0x980c30f0, + 0x55f9de35, + 0xf40612b8, + 0x0b98e41e, + 0xef0c98ee, + 0xf102cbbb, + 0xb607c4b7, + 0xbbcf06b4, + 0xfcd0fc00, + 0x4221f5e0, +/* 0x072f: memx_info */ + 0xf100f803, + 0xf103c0c7, + 0xf50800b7, + 0xf8034221, +/* 0x073d: memx_recv */ + 0x01d6b000, + 0xb0a90bf4, + 0x0bf400d6, +/* 0x074b: memx_init */ + 0xf800f8e9, +/* 0x074d: perf_recv */ +/* 0x074f: perf_init */ + 0xf800f800, +/* 0x0751: i2c_drive_scl */ + 0x0036b000, + 0xf1110bf4, + 0xb607e007, + 0x01d00604, + 0xf804bd00, +/* 0x0765: i2c_drive_scl_lo */ + 0xe407f100, 0x0604b607, - 0xbd0006d0, -/* 0x04a5: memx_func_leave_wait */ - 0xc067f104, - 0x0664b607, - 0xf00066cf, - 0x1bf40464, -/* 0x04b7: memx_func_wr32 */ - 0x9800f8f3, - 0x15980016, - 0x0810b601, - 0x50f960f9, - 0xe0fcd0fc, - 0xb63f21f4, - 0x1bf40242, -/* 0x04d3: memx_func_wait */ - 0xf000f8e9, - 0x84b62c87, - 0x0088cf06, - 0x98001e98, - 0x1c98011d, - 0x031b9802, - 0xf41010b6, - 0x00f89c21, -/* 0x04f0: memx_func_delay */ - 0xb6001e98, - 0x21f40410, -/* 0x04fb: memx_exec */ - 0xf900f87f, - 0xb9d0f9e0, - 0xb2b902c1, -/* 0x0505: memx_exec_next */ - 0x00139802, - 0x950410b6, - 0x30f01034, - 0xde35980c, - 0x12b855f9, - 0xec1ef406, - 0xe0fcd0fc, - 0x02b921f5, -/* 0x0526: memx_info */ - 0xc7f100f8, - 0xb7f103ac, - 0x21f50800, - 0x00f802b9, -/* 0x0534: memx_recv */ - 0xf401d6b0, - 0xd6b0c40b, - 0xe90bf400, -/* 0x0542: memx_init */ - 0x00f800f8, -/* 0x0544: perf_recv */ -/* 0x0546: perf_init */ - 0x00f800f8, -/* 0x0548: i2c_drive_scl */ - 0xf40036b0, - 0x07f1110b, - 0x04b607e0, - 0x0001d006, - 0x00f804bd, -/* 0x055c: i2c_drive_scl_lo */ - 0x07e407f1, - 0xd00604b6, - 0x04bd0001, -/* 0x056a: i2c_drive_sda */ - 0x36b000f8, - 0x110bf400, - 0x07e007f1, - 0xd00604b6, - 0x04bd0002, -/* 0x057e: i2c_drive_sda_lo */ - 0x07f100f8, - 0x04b607e4, - 0x0002d006, - 0x00f804bd, -/* 0x058c: i2c_sense_scl */ - 0xf10132f4, - 0xb607c437, - 0x33cf0634, - 0x0431fd00, - 0xf4060bf4, -/* 0x05a2: i2c_sense_scl_done */ - 0x00f80131, -/* 0x05a4: i2c_sense_sda */ - 0xf10132f4, - 0xb607c437, - 0x33cf0634, - 0x0432fd00, - 0xf4060bf4, -/* 0x05ba: i2c_sense_sda_done */ - 0x00f80131, -/* 0x05bc: i2c_raise_scl */ - 0x47f140f9, - 0x37f00898, - 0x4821f501, -/* 0x05c9: i2c_raise_scl_wait */ - 0xe8e7f105, - 0x7f21f403, - 0x058c21f5, - 0xb60901f4, - 0x1bf40142, -/* 0x05dd: i2c_raise_scl_done */ - 0xf840fcef, -/* 0x05e1: i2c_start */ - 0x8c21f500, - 0x0d11f405, - 0x05a421f5, - 0xf40611f4, -/* 0x05f2: i2c_start_rep */ - 0x37f0300e, - 0x4821f500, - 0x0137f005, - 0x056a21f5, - 0xb60076bb, - 0x50f90465, - 0xbb046594, - 0x50bd0256, - 0xfc0475fd, - 0xbc21f550, - 0x0464b605, -/* 0x061f: i2c_start_send */ - 0xf01f11f4, + 0xbd0001d0, +/* 0x0773: i2c_drive_sda */ + 0xb000f804, + 0x0bf40036, + 0xe007f111, + 0x0604b607, + 0xbd0002d0, +/* 0x0787: i2c_drive_sda_lo */ + 0xf100f804, + 0xb607e407, + 0x02d00604, + 0xf804bd00, +/* 0x0795: i2c_sense_scl */ + 0x0132f400, + 0x07c437f1, + 0xcf0634b6, + 0x31fd0033, + 0x060bf404, +/* 0x07ab: i2c_sense_scl_done */ + 0xf80131f4, +/* 0x07ad: i2c_sense_sda */ + 0x0132f400, + 0x07c437f1, + 0xcf0634b6, + 0x32fd0033, + 0x060bf404, +/* 0x07c3: i2c_sense_sda_done */ + 0xf80131f4, +/* 0x07c5: i2c_raise_scl */ + 0xf140f900, + 0xf0089847, + 0x21f50137, +/* 0x07d2: i2c_raise_scl_wait */ + 0xe7f10751, + 0x21f403e8, + 0x9521f57f, + 0x0901f407, + 0xf40142b6, +/* 0x07e6: i2c_raise_scl_done */ + 0x40fcef1b, +/* 0x07ea: i2c_start */ + 0x21f500f8, + 0x11f40795, + 0xad21f50d, + 0x0611f407, +/* 0x07fb: i2c_start_rep */ + 0xf0300ef4, 0x21f50037, - 0xe7f1056a, - 0x21f41388, - 0x0037f07f, - 0x054821f5, - 0x1388e7f1, -/* 0x063b: i2c_start_out */ - 0xf87f21f4, -/* 0x063d: i2c_stop */ - 0x0037f000, - 0x054821f5, + 0x37f00751, + 0x7321f501, + 0x0076bb07, + 0xf90465b6, + 0x04659450, + 0xbd0256bb, + 0x0475fd50, + 0x21f550fc, + 0x64b607c5, + 0x1f11f404, +/* 0x0828: i2c_start_send */ 0xf50037f0, - 0xf1056a21, - 0xf403e8e7, + 0xf1077321, + 0xf41388e7, 0x37f07f21, - 0x4821f501, - 0x88e7f105, + 0x5121f500, + 0x88e7f107, 0x7f21f413, - 0xf50137f0, - 0xf1056a21, - 0xf41388e7, - 0x00f87f21, -/* 0x0670: i2c_bitw */ - 0x056a21f5, +/* 0x0844: i2c_start_out */ +/* 0x0846: i2c_stop */ + 0x37f000f8, + 0x5121f500, + 0x0037f007, + 0x077321f5, 0x03e8e7f1, - 0xbb7f21f4, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x05bc21f5, - 0xf40464b6, - 0xe7f11811, + 0xf07f21f4, + 0x21f50137, + 0xe7f10751, 0x21f41388, - 0x0037f07f, - 0x054821f5, + 0x0137f07f, + 0x077321f5, 0x1388e7f1, -/* 0x06af: i2c_bitw_out */ 0xf87f21f4, -/* 0x06b1: i2c_bitr */ - 0x0137f000, - 0x056a21f5, - 0x03e8e7f1, - 0xbb7f21f4, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x05bc21f5, - 0xf40464b6, - 0x21f51b11, - 0x37f005a4, - 0x4821f500, - 0x88e7f105, +/* 0x0879: i2c_bitw */ + 0x7321f500, + 0xe8e7f107, + 0x7f21f403, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0xc521f550, + 0x0464b607, + 0xf11811f4, + 0xf41388e7, + 0x37f07f21, + 0x5121f500, + 0x88e7f107, 0x7f21f413, - 0xf4013cf0, -/* 0x06f6: i2c_bitr_done */ - 0x00f80131, -/* 0x06f8: i2c_get_byte */ - 0xf00057f0, -/* 0x06fe: i2c_get_byte_next */ - 0x54b60847, +/* 0x08b8: i2c_bitw_out */ +/* 0x08ba: i2c_bitr */ + 0x37f000f8, + 0x7321f501, + 0xe8e7f107, + 0x7f21f403, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0xc521f550, + 0x0464b607, + 0xf51b11f4, + 0xf007ad21, + 0x21f50037, + 0xe7f10751, + 0x21f41388, + 0x013cf07f, +/* 0x08ff: i2c_bitr_done */ + 0xf80131f4, +/* 0x0901: i2c_get_byte */ + 0x0057f000, +/* 0x0907: i2c_get_byte_next */ + 0xb60847f0, + 0x76bb0154, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb608ba21, + 0x11f40464, + 0x0553fd2b, + 0xf40142b6, + 0x37f0d81b, 0x0076bb01, 0xf90465b6, 0x04659450, 0xbd0256bb, 0x0475fd50, 0x21f550fc, - 0x64b606b1, - 0x2b11f404, - 0xb60553fd, - 0x1bf40142, - 0x0137f0d8, - 0xb60076bb, - 0x50f90465, - 0xbb046594, - 0x50bd0256, - 0xfc0475fd, - 0x7021f550, - 0x0464b606, -/* 0x0748: i2c_get_byte_done */ -/* 0x074a: i2c_put_byte */ - 0x47f000f8, -/* 0x074d: i2c_put_byte_next */ - 0x0142b608, - 0xbb3854ff, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x067021f5, - 0xf40464b6, - 0x46b03411, - 0xd81bf400, + 0x64b60879, +/* 0x0951: i2c_get_byte_done */ +/* 0x0953: i2c_put_byte */ + 0xf000f804, +/* 0x0956: i2c_put_byte_next */ + 0x42b60847, + 0x3854ff01, 0xb60076bb, 0x50f90465, 0xbb046594, 0x50bd0256, 0xfc0475fd, - 0xb121f550, - 0x0464b606, - 0xbb0f11f4, - 0x36b00076, - 0x061bf401, -/* 0x07a3: i2c_put_byte_done */ - 0xf80132f4, -/* 0x07a5: i2c_addr */ - 0x0076bb00, + 0x7921f550, + 0x0464b608, + 0xb03411f4, + 0x1bf40046, + 0x0076bbd8, 0xf90465b6, 0x04659450, 0xbd0256bb, 0x0475fd50, 0x21f550fc, - 0x64b605e1, - 0x2911f404, - 0x012ec3e7, - 0xfd0134b6, - 0x76bb0553, + 0x64b608ba, + 0x0f11f404, + 0xb00076bb, + 0x1bf40136, + 0x0132f406, +/* 0x09ac: i2c_put_byte_done */ +/* 0x09ae: i2c_addr */ + 0x76bb00f8, 0x0465b600, 0x659450f9, 0x0256bb04, 0x75fd50bd, 0xf550fc04, - 0xb6074a21, -/* 0x07ea: i2c_addr_done */ - 0x00f80464, -/* 0x07ec: i2c_acquire_addr */ - 0xb6f8cec7, - 0xe0b702e4, - 0xee980bfc, -/* 0x07fb: i2c_acquire */ - 0xf500f800, - 0xf407ec21, - 0xd9f00421, - 0x3f21f403, -/* 0x080a: i2c_release */ - 0x21f500f8, - 0x21f407ec, - 0x03daf004, - 0xf83f21f4, -/* 0x0819: i2c_recv */ - 0x0132f400, - 0xb6f8c1c7, - 0x16b00214, - 0x3a1ff528, - 0xd413a001, - 0x0032980b, - 0x0bac13a0, - 0xf4003198, - 0xd0f90231, - 0xd0f9e0f9, - 0x000067f1, - 0x100063f1, - 0xbb016792, + 0xb607ea21, + 0x11f40464, + 0x2ec3e729, + 0x0134b601, + 0xbb0553fd, 0x65b60076, 0x9450f904, 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x07fb21f5, - 0xfc0464b6, - 0x00d6b0d0, - 0x00b31bf5, - 0xbb0057f0, + 0x095321f5, +/* 0x09f3: i2c_addr_done */ + 0xf80464b6, +/* 0x09f5: i2c_acquire_addr */ + 0xf8cec700, + 0xb702e4b6, + 0x980c10e0, + 0x00f800ee, +/* 0x0a04: i2c_acquire */ + 0x09f521f5, + 0xf00421f4, + 0x21f403d9, +/* 0x0a13: i2c_release */ + 0xf500f83f, + 0xf409f521, + 0xdaf00421, + 0x3f21f403, +/* 0x0a22: i2c_recv */ + 0x32f400f8, + 0xf8c1c701, + 0xb00214b6, + 0x1ff52816, + 0x13a0013a, + 0x32980be8, + 0xc013a000, + 0x0031980b, + 0xf90231f4, + 0xf9e0f9d0, + 0x0067f1d0, + 0x0063f100, + 0x01679210, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x0421f550, + 0x0464b60a, + 0xd6b0d0fc, + 0xb31bf500, + 0x0057f000, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0xae21f550, + 0x0464b609, + 0x00d011f5, + 0xbbe0c5c7, 0x65b60076, 0x9450f904, 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x07a521f5, + 0x095321f5, 0xf50464b6, - 0xc700d011, - 0x76bbe0c5, + 0xf000ad11, + 0x76bb0157, 0x0465b600, 0x659450f9, 0x0256bb04, 0x75fd50bd, 0xf550fc04, - 0xb6074a21, + 0xb609ae21, 0x11f50464, - 0x57f000ad, - 0x0076bb01, - 0xf90465b6, - 0x04659450, - 0xbd0256bb, - 0x0475fd50, - 0x21f550fc, - 0x64b607a5, - 0x8a11f504, - 0x0076bb00, - 0xf90465b6, - 0x04659450, - 0xbd0256bb, - 0x0475fd50, - 0x21f550fc, - 0x64b606f8, - 0x6a11f404, - 0xbbe05bcb, - 0x65b60076, - 0x9450f904, - 0x56bb0465, - 0xfd50bd02, - 0x50fc0475, - 0x063d21f5, - 0xb90464b6, - 0x74bd025b, -/* 0x091f: i2c_recv_not_rd08 */ - 0xb0430ef4, - 0x1bf401d6, - 0x0057f03d, - 0x07a521f5, - 0xc73311f4, - 0x21f5e0c5, - 0x11f4074a, - 0x0057f029, - 0x07a521f5, - 0xc71f11f4, - 0x21f5e0b5, - 0x11f4074a, - 0x3d21f515, - 0xc774bd06, - 0x1bf408c5, - 0x0232f409, -/* 0x095f: i2c_recv_not_wr08 */ -/* 0x095f: i2c_recv_done */ - 0xc7030ef4, - 0x21f5f8ce, - 0xe0fc080a, - 0x12f4d0fc, - 0x027cb90a, - 0x02b921f5, -/* 0x0974: i2c_recv_exit */ -/* 0x0976: i2c_init */ + 0x76bb008a, + 0x0465b600, + 0x659450f9, + 0x0256bb04, + 0x75fd50bd, + 0xf550fc04, + 0xb6090121, + 0x11f40464, + 0xe05bcb6a, + 0xb60076bb, + 0x50f90465, + 0xbb046594, + 0x50bd0256, + 0xfc0475fd, + 0x4621f550, + 0x0464b608, + 0xbd025bb9, + 0x430ef474, +/* 0x0b28: i2c_recv_not_rd08 */ + 0xf401d6b0, + 0x57f03d1b, + 0xae21f500, + 0x3311f409, + 0xf5e0c5c7, + 0xf4095321, + 0x57f02911, + 0xae21f500, + 0x1f11f409, + 0xf5e0b5c7, + 0xf4095321, + 0x21f51511, + 0x74bd0846, + 0xf408c5c7, + 0x32f4091b, + 0x030ef402, +/* 0x0b68: i2c_recv_not_wr08 */ +/* 0x0b68: i2c_recv_done */ + 0xf5f8cec7, + 0xfc0a1321, + 0xf4d0fce0, + 0x7cb90a12, + 0x4221f502, +/* 0x0b7d: i2c_recv_exit */ +/* 0x0b7f: i2c_init */ + 0xf800f803, +/* 0x0b81: test_recv */ + 0xd817f100, + 0x0614b605, + 0xb60011cf, + 0x07f10110, + 0x04b605d8, + 0x0001d006, + 0xe7f104bd, + 0xe3f1d900, + 0x21f5134f, + 0x00f80262, +/* 0x0ba8: test_init */ + 0x0800e7f1, + 0x026221f5, +/* 0x0bb2: idle_recv */ 0x00f800f8, -/* 0x0978: test_recv */ - 0x05d817f1, - 0xcf0614b6, - 0x10b60011, - 0xd807f101, - 0x0604b605, - 0xbd0001d0, - 0x00e7f104, - 0x4fe3f1d9, - 0xf521f513, -/* 0x099f: test_init */ - 0xf100f801, - 0xf50800e7, - 0xf801f521, -/* 0x09a9: idle_recv */ -/* 0x09ab: idle */ - 0xf400f800, - 0x17f10031, - 0x14b605d4, - 0x0011cf06, - 0xf10110b6, - 0xb605d407, - 0x01d00604, -/* 0x09c7: idle_loop */ - 0xf004bd00, - 0x32f45817, -/* 0x09cd: idle_proc */ -/* 0x09cd: idle_proc_exec */ - 0xb910f902, - 0x21f5021e, - 0x10fc02c2, - 0xf40911f4, - 0x0ef40231, -/* 0x09e1: idle_proc_next */ - 0x5810b6ef, - 0xf4061fb8, - 0x02f4e61b, - 0x0028f4dd, - 0x00bb0ef4, - 0x00000000, - 0x00000000, +/* 0x0bb4: idle */ + 0xf10031f4, + 0xb605d417, + 0x11cf0614, + 0x0110b600, + 0x05d407f1, + 0xd00604b6, + 0x04bd0001, +/* 0x0bd0: idle_loop */ + 0xf45817f0, +/* 0x0bd6: idle_proc */ +/* 0x0bd6: idle_proc_exec */ + 0x10f90232, + 0xf5021eb9, + 0xfc034b21, + 0x0911f410, + 0xf40231f4, +/* 0x0bea: idle_proc_next */ + 0x10b6ef0e, + 0x061fb858, + 0xf4e61bf4, + 0x28f4dd02, + 0xbb0ef400, 0x00000000, }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc index 8a89dfe41ce1ba8ddf7b808ef5534e5eefd418a9..b85443261569aaec41f786c3c31e72103a31af98 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc @@ -23,6 +23,7 @@ */ #define NVKM_PPWR_CHIPSET GF119 +#define HW_TICKS_PER_US 324 //#define NVKM_FALCON_PC24 #define NVKM_FALCON_UNSHIFTED_IO @@ -34,6 +35,7 @@ .section #nvd0_pwr_data #define INCLUDE_PROC #include "kernel.fuc" +#include "arith.fuc" #include "host.fuc" #include "memx.fuc" #include "perf.fuc" @@ -44,6 +46,7 @@ #define INCLUDE_DATA #include "kernel.fuc" +#include "arith.fuc" #include "host.fuc" #include "memx.fuc" #include "perf.fuc" @@ -56,6 +59,7 @@ .section #nvd0_pwr_code #define INCLUDE_CODE #include "kernel.fuc" +#include "arith.fuc" #include "host.fuc" #include "memx.fuc" #include "perf.fuc" diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc.h index 8d369b3faabaea2492fef19f55e4ee15ed30166d..12d86f72ad105a191f152d38c1f0b4363c856ba1 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc.h @@ -24,8 +24,8 @@ uint32_t nvd0_pwr_data[] = { 0x00000000, /* 0x0058: proc_list_head */ 0x54534f48, - 0x000003be, - 0x00000367, + 0x0000049d, + 0x00000446, 0x00000000, 0x00000000, 0x00000000, @@ -46,8 +46,8 @@ uint32_t nvd0_pwr_data[] = { 0x00000000, 0x00000000, 0x584d454d, - 0x000004b8, - 0x000004aa, + 0x00000678, + 0x0000066a, 0x00000000, 0x00000000, 0x00000000, @@ -68,8 +68,8 @@ uint32_t nvd0_pwr_data[] = { 0x00000000, 0x00000000, 0x46524550, - 0x000004bc, - 0x000004ba, + 0x0000067c, + 0x0000067a, 0x00000000, 0x00000000, 0x00000000, @@ -90,8 +90,8 @@ uint32_t nvd0_pwr_data[] = { 0x00000000, 0x00000000, 0x5f433249, - 0x000008d7, - 0x0000077a, + 0x00000a97, + 0x0000093a, 0x00000000, 0x00000000, 0x00000000, @@ -112,8 +112,8 @@ uint32_t nvd0_pwr_data[] = { 0x00000000, 0x00000000, 0x54534554, - 0x000008fa, - 0x000008d9, + 0x00000aba, + 0x00000a99, 0x00000000, 0x00000000, 0x00000000, @@ -134,8 +134,8 @@ uint32_t nvd0_pwr_data[] = { 0x00000000, 0x00000000, 0x454c4449, - 0x00000906, - 0x00000904, + 0x00000ac6, + 0x00000ac4, 0x00000000, 0x00000000, 0x00000000, @@ -227,24 +227,31 @@ uint32_t nvd0_pwr_data[] = { 0x00000000, 0x00000000, /* 0x0370: memx_func_head */ - 0x00010000, - 0x00000000, - 0x000003f4, -/* 0x037c: memx_func_next */ 0x00000001, 0x00000000, - 0x00000415, + 0x000004d3, +/* 0x037c: memx_func_next */ 0x00000002, + 0x00000000, + 0x00000554, + 0x00000003, 0x00000002, - 0x00000430, - 0x00040003, + 0x000005d8, + 0x00040004, + 0x00000000, + 0x000005f4, + 0x00010005, + 0x00000000, + 0x0000060e, + 0x00010006, + 0x00000000, + 0x000005d3, +/* 0x03b8: memx_func_tail */ +/* 0x03b8: memx_ts_start */ 0x00000000, - 0x0000044c, - 0x00010004, +/* 0x03bc: memx_ts_end */ 0x00000000, - 0x00000466, -/* 0x03ac: memx_func_tail */ -/* 0x03ac: memx_data_head */ +/* 0x03c0: memx_data_head */ 0x00000000, 0x00000000, 0x00000000, @@ -757,8 +764,8 @@ uint32_t nvd0_pwr_data[] = { 0x00000000, 0x00000000, 0x00000000, -/* 0x0bac: memx_data_tail */ -/* 0x0bac: i2c_scl_map */ +/* 0x0bc0: memx_data_tail */ +/* 0x0bc0: i2c_scl_map */ 0x00000400, 0x00000800, 0x00001000, @@ -769,7 +776,7 @@ uint32_t nvd0_pwr_data[] = { 0x00020000, 0x00040000, 0x00080000, -/* 0x0bd4: i2c_sda_map */ +/* 0x0be8: i2c_sda_map */ 0x00100000, 0x00200000, 0x00400000, @@ -781,10 +788,69 @@ uint32_t nvd0_pwr_data[] = { 0x10000000, 0x20000000, 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, }; uint32_t nvd0_pwr_code[] = { - 0x02bf0ef5, + 0x034d0ef5, /* 0x0004: rd32 */ 0x07a007f1, 0xbd000ed0, @@ -814,17 +880,20 @@ uint32_t nvd0_pwr_code[] = { 0xd4f100dd, 0x1bf47000, /* 0x0067: nsec */ - 0xf000f8f5, + 0xf900f8f5, + 0xf080f990, 0x88cf2c87, -/* 0x006d: nsec_loop */ +/* 0x0071: nsec_loop */ 0x2c97f000, 0xbb0099cf, 0x9eb80298, 0xf41ef406, -/* 0x007e: wait */ - 0x87f000f8, + 0x90fc80fc, +/* 0x0086: wait */ + 0x90f900f8, + 0x87f080f9, 0x0088cf2c, -/* 0x0084: wait_loop */ +/* 0x0090: wait_loop */ 0xf402eeb9, 0xdab90421, 0x04adfd02, @@ -833,28 +902,29 @@ uint32_t nvd0_pwr_code[] = { 0x0099cf2c, 0xb80298bb, 0x1ef4069b, -/* 0x00a5: wait_done */ -/* 0x00a7: intr_watchdog */ - 0x9800f8e2, +/* 0x00b1: wait_done */ + 0xfc80fce2, +/* 0x00b7: intr_watchdog */ + 0x9800f890, 0x96b003e9, 0x2a0bf400, 0xbb9a0a98, 0x1cf4029a, 0x01d7f00f, - 0x020621f5, + 0x028c21f5, 0x0ef494bd, -/* 0x00c5: intr_watchdog_next_time */ +/* 0x00d5: intr_watchdog_next_time */ 0x9b0a9815, 0xf400a6b0, 0x9ab8090b, 0x061cf406, -/* 0x00d4: intr_watchdog_next_time_set */ -/* 0x00d7: intr_watchdog_next_proc */ +/* 0x00e4: intr_watchdog_next_time_set */ +/* 0x00e7: intr_watchdog_next_proc */ 0x809b0980, 0xe0b603e9, 0x68e6b158, 0xc61bf402, -/* 0x00e6: intr */ +/* 0x00f6: intr */ 0x00f900f8, 0x80f904bd, 0xa0f990f9, @@ -872,12 +942,12 @@ uint32_t nvd0_pwr_code[] = { 0x0bf40289, 0x9b008020, 0xf458e7f0, - 0x0998a721, + 0x0998b721, 0x0096b09b, 0xf00e0bf4, 0x09d03407, 0x8004bd00, -/* 0x013e: intr_skip_watchdog */ +/* 0x014e: intr_skip_watchdog */ 0x89e49a09, 0x0bf40800, 0x8897f13c, @@ -889,20 +959,20 @@ uint32_t nvd0_pwr_code[] = { 0xf14f48e7, 0xf05453e3, 0x21f500d7, - 0xc0fc026b, + 0xc0fc02f1, 0x04c007f1, 0xbd000cd0, -/* 0x0175: intr_subintr_skip_fifo */ +/* 0x0185: intr_subintr_skip_fifo */ 0x8807f104, 0x0009d006, -/* 0x017e: intr_skip_subintr */ +/* 0x018e: intr_skip_subintr */ 0x89c404bd, 0x070bf420, 0xffbfa4f1, -/* 0x0188: intr_skip_pause */ +/* 0x0198: intr_skip_pause */ 0xf44089c4, 0xa4f1070b, -/* 0x0192: intr_skip_user0 */ +/* 0x01a2: intr_skip_user0 */ 0x07f0ffbf, 0x0008d004, 0x80fc04bd, @@ -912,189 +982,298 @@ uint32_t nvd0_pwr_code[] = { 0xfca0fcb0, 0xfc80fc90, 0x0032f400, -/* 0x01b6: timer */ - 0x32f401f8, - 0x03f89810, - 0xf40086b0, - 0xfe80421c, - 0x3807f003, +/* 0x01c6: ticks_from_ns */ + 0xc0f901f8, + 0xd7f1b0f9, + 0xd3f00144, + 0xb321f500, + 0xe8ccec03, + 0x00b4b003, + 0xec120bf4, + 0xf103e8ee, + 0xf00144d7, + 0x21f500d3, +/* 0x01ee: ticks_from_ns_quit */ + 0xceb903b3, + 0xfcb0fc02, +/* 0x01f7: ticks_from_us */ + 0xf900f8c0, + 0xf1b0f9c0, + 0xf00144d7, + 0x21f500d3, + 0xceb903b3, + 0x00b4b002, + 0xbd050bf4, +/* 0x0211: ticks_from_us_quit */ + 0xfcb0fce4, +/* 0x0217: ticks_to_us */ + 0xf100f8c0, + 0xf00144d7, + 0xedff00d3, +/* 0x0223: timer */ + 0xf900f8ec, + 0xf480f990, + 0xf8981032, + 0x0086b003, + 0xbd531cf4, + 0x3807f084, 0xbd0008d0, - 0x0887f004, - 0xf00088cf, - 0x1bf40284, - 0x3487f020, - 0xb80088cf, - 0x0bf406e0, - 0x06e8b809, -/* 0x01eb: timer_reset */ - 0xf0191ef4, - 0x0ed03407, - 0x8004bd00, -/* 0x01f6: timer_enable */ - 0x87f09a0e, - 0x3807f001, - 0xbd0008d0, -/* 0x0201: timer_done */ - 0x1031f404, -/* 0x0206: send_proc */ - 0x80f900f8, - 0xe89890f9, + 0x3487f004, + 0x980088cf, + 0x98bb9a09, + 0x00e9bb02, + 0xf003fe80, + 0x88cf0887, + 0x0284f000, + 0xf0201bf4, + 0x88cf3487, + 0x06e0b800, + 0xb8090bf4, + 0x1cf406e8, +/* 0x026d: timer_reset */ + 0x3407f00e, + 0xbd000ed0, + 0x9a0e8004, +/* 0x0278: timer_enable */ + 0xf00187f0, + 0x08d03807, +/* 0x0283: timer_done */ + 0xf404bd00, + 0x80fc1031, + 0x00f890fc, +/* 0x028c: send_proc */ + 0x90f980f9, + 0x9805e898, + 0x86f004e9, + 0x0689b804, + 0xc42a0bf4, + 0x88940398, + 0x1880b604, + 0x98008ebb, + 0x8a8000fa, + 0x018d8000, + 0x80028c80, + 0x90b6038b, + 0x0794f001, + 0xf404e980, +/* 0x02c6: send_done */ + 0x90fc0231, + 0x00f880fc, +/* 0x02cc: find */ + 0x87f080f9, + 0x0131f458, +/* 0x02d4: find_loop */ + 0xb8008a98, + 0x0bf406ae, + 0x5880b610, + 0x026886b1, + 0xf4f01bf4, +/* 0x02ea: find_done */ + 0x8eb90132, + 0xf880fc02, +/* 0x02f1: send */ + 0xcc21f500, + 0x9701f402, +/* 0x02fa: recv */ + 0x90f900f8, + 0xe89880f9, 0x04e99805, - 0xb80486f0, + 0xb80132f4, 0x0bf40689, - 0x0398c42a, - 0xb6048894, - 0x8ebb1880, - 0x00fa9800, - 0x80008a80, - 0x8c80018d, - 0x038b8002, - 0xf00190b6, - 0xe9800794, - 0x0231f404, -/* 0x0240: send_done */ - 0x80fc90fc, -/* 0x0246: find */ - 0x80f900f8, - 0xf45887f0, -/* 0x024e: find_loop */ - 0x8a980131, - 0x06aeb800, - 0xb6100bf4, - 0x86b15880, - 0x1bf40268, - 0x0132f4f0, -/* 0x0264: find_done */ - 0xfc028eb9, -/* 0x026b: send */ - 0xf500f880, - 0xf4024621, - 0x00f89701, -/* 0x0274: recv */ - 0x9805e898, - 0x32f404e9, - 0x0689b801, - 0xc43d0bf4, - 0x80b60389, - 0x0784f001, - 0x9805e880, - 0xf0f902ea, - 0xf9018ffe, - 0x02efb9f0, - 0xbb049994, - 0xe0b600e9, - 0x03eb9818, - 0x9802ec98, - 0xee9801ed, - 0xfca5f900, - 0x00f8fef0, - 0xfc0131f4, -/* 0x02bd: recv_done */ -/* 0x02bf: init */ - 0xf100f8f0, - 0xcf010817, - 0x11e70011, - 0x14b60109, - 0x0014fe08, - 0x00e017f1, - 0xf00013f0, - 0x01d01c07, - 0xf004bd00, - 0x07f0ff17, - 0x0001d014, - 0x17f004bd, - 0x0015f102, - 0x1007f008, - 0xbd0001d0, - 0xe617f104, - 0x0013f000, - 0xf40010fe, - 0x17f01031, - 0x3807f001, - 0xbd0001d0, - 0x58f7f004, -/* 0x0314: init_proc */ - 0xb001f198, - 0x0bf40016, - 0xb615f9fa, - 0x0ef458f0, -/* 0x0325: host_send */ - 0xb017f1f2, - 0x0011cf04, - 0x04a027f1, - 0xb80022cf, - 0x0bf40612, - 0x071ec42f, - 0xb704ee94, - 0x980270e0, + 0x0389c43d, + 0xf00180b6, + 0xe8800784, + 0x02ea9805, + 0x8ffef0f9, + 0xb9f0f901, + 0x999402ef, + 0x00e9bb04, + 0x9818e0b6, 0xec9803eb, 0x01ed9802, - 0xf500ee98, - 0xb6026b21, - 0x1ec40110, - 0xb007f10f, - 0x000ed004, - 0x0ef404bd, -/* 0x0365: host_send_done */ -/* 0x0367: host_recv */ - 0xf100f8c3, - 0xf14e4917, - 0xb8525413, - 0x0bf406e1, -/* 0x0375: host_recv_wait */ - 0xcc17f1b3, - 0x0011cf04, - 0x04c827f1, - 0xf00022cf, - 0x12b80816, - 0xec0bf406, - 0xb60723c4, - 0x30b70434, - 0x3b8002f0, - 0x023c8003, - 0x80013d80, - 0x20b6003e, - 0x0f24f001, - 0x04c807f1, - 0xbd0002d0, - 0x4027f004, - 0xd00007f0, - 0x04bd0002, -/* 0x03be: host_init */ + 0xf900ee98, + 0xfef0fca5, + 0x31f400f8, +/* 0x0347: recv_done */ + 0xfcf0fc01, + 0xf890fc80, +/* 0x034d: init */ + 0x0817f100, + 0x0011cf01, + 0x010911e7, + 0xfe0814b6, + 0x17f10014, + 0x13f000e0, + 0x1c07f000, + 0xbd0001d0, + 0xff17f004, + 0xd01407f0, + 0x04bd0001, + 0xf10217f0, + 0xf0080015, + 0x01d01007, + 0xf104bd00, + 0xf000f617, + 0x10fe0013, + 0x1031f400, + 0xf00117f0, + 0x01d03807, + 0xf004bd00, +/* 0x03a2: init_proc */ + 0xf19858f7, + 0x0016b001, + 0xf9fa0bf4, + 0x58f0b615, +/* 0x03b3: mulu32_32_64 */ + 0xf9f20ef4, + 0xf920f910, + 0x9540f930, + 0xd29510e1, + 0xbdc4bd10, + 0xc0edffb4, + 0xb9301dff, + 0x34f10234, + 0x34b6ffff, + 0x1045b610, + 0xbb00c3bb, + 0xe2ff01b4, + 0x0234b930, + 0xffff34f1, + 0xb61034b6, + 0xc3bb1045, + 0x01b4bb00, + 0xbb3012ff, + 0x40fc00b3, + 0x20fc30fc, + 0x00f810fc, +/* 0x0404: host_send */ + 0x04b017f1, + 0xf10011cf, + 0xcf04a027, + 0x12b80022, + 0x2f0bf406, + 0x94071ec4, + 0xe0b704ee, + 0xeb980270, + 0x02ec9803, + 0x9801ed98, + 0x21f500ee, + 0x10b602f1, + 0x0f1ec401, + 0x04b007f1, + 0xbd000ed0, + 0xc30ef404, +/* 0x0444: host_send_done */ +/* 0x0446: host_recv */ 0x17f100f8, - 0x14b60080, - 0x7015f110, - 0xd007f102, - 0x0001d004, - 0x17f104bd, - 0x14b60080, - 0xf015f110, - 0xdc07f102, - 0x0001d004, - 0x17f004bd, - 0xc407f101, - 0x0001d004, - 0x00f804bd, -/* 0x03f4: memx_func_enter */ + 0x13f14e49, + 0xe1b85254, + 0xb30bf406, +/* 0x0454: host_recv_wait */ + 0x04cc17f1, + 0xf10011cf, + 0xcf04c827, + 0x16f00022, + 0x0612b808, + 0xc4ec0bf4, + 0x34b60723, + 0xf030b704, + 0x033b8002, + 0x80023c80, + 0x3e80013d, + 0x0120b600, + 0xf10f24f0, + 0xd004c807, + 0x04bd0002, + 0xf04027f0, + 0x02d00007, + 0xf804bd00, +/* 0x049d: host_init */ + 0x8017f100, + 0x1014b600, + 0x027015f1, + 0x04d007f1, + 0xbd0001d0, + 0x8017f104, + 0x1014b600, + 0x02f015f1, + 0x04dc07f1, + 0xbd0001d0, + 0x0117f004, + 0x04c407f1, + 0xbd0001d0, +/* 0x04d3: memx_func_enter */ + 0xf100f804, + 0xf1162067, + 0xf1f55d77, + 0xb9ffff73, + 0x21f4026e, + 0x02d8b904, + 0xf90487fd, + 0xfc80f960, + 0xf4e0fcd0, + 0x77f13321, + 0x73f1fffe, + 0x6eb9ffff, + 0x0421f402, + 0xfd02d8b9, + 0x60f90487, + 0xd0fc80f9, + 0x21f4e0fc, + 0xf067f133, + 0x026eb926, + 0xb90421f4, + 0x87fd02d8, + 0xf960f904, + 0xfcd0fc80, + 0x3321f4e0, 0xf10467f0, 0xd007e007, 0x04bd0006, -/* 0x0400: memx_func_enter_wait */ +/* 0x053c: memx_func_enter_wait */ 0x07c067f1, 0xf00066cf, 0x0bf40464, - 0x001698f6, - 0xf80410b6, -/* 0x0415: memx_func_leave */ - 0x0467f000, + 0x2c67f0f6, + 0x800066cf, + 0x00f8ee06, +/* 0x0554: memx_func_leave */ + 0xcf2c67f0, + 0x06800066, + 0x0467f0ef, 0x07e407f1, 0xbd0006d0, -/* 0x0421: memx_func_leave_wait */ +/* 0x0569: memx_func_leave_wait */ 0xc067f104, 0x0066cf07, 0xf40464f0, - 0x00f8f61b, -/* 0x0430: memx_func_wr32 */ + 0x67f1f61b, + 0x77f126f0, + 0x73f00001, + 0x026eb900, + 0xb90421f4, + 0x87fd02d8, + 0xf960f905, + 0xfcd0fc80, + 0x3321f4e0, + 0x162067f1, + 0xf4026eb9, + 0xd8b90421, + 0x0587fd02, + 0x80f960f9, + 0xe0fcd0fc, + 0xf13321f4, + 0xf00aa277, + 0x6eb90073, + 0x0421f402, + 0xfd02d8b9, + 0x60f90587, + 0xd0fc80f9, + 0x21f4e0fc, +/* 0x05d3: memx_func_wait_vblank */ + 0xb600f833, + 0x00f80410, +/* 0x05d8: memx_func_wr32 */ 0x98001698, 0x10b60115, 0xf960f908, @@ -1102,131 +1281,137 @@ uint32_t nvd0_pwr_code[] = { 0x3321f4e0, 0xf40242b6, 0x00f8e91b, -/* 0x044c: memx_func_wait */ +/* 0x05f4: memx_func_wait */ 0xcf2c87f0, 0x1e980088, 0x011d9800, 0x98021c98, 0x10b6031b, - 0x7e21f410, -/* 0x0466: memx_func_delay */ + 0x8621f410, +/* 0x060e: memx_func_delay */ 0x1e9800f8, 0x0410b600, 0xf86721f4, -/* 0x0471: memx_exec */ +/* 0x0619: memx_exec */ 0xf9e0f900, 0x02c1b9d0, -/* 0x047b: memx_exec_next */ +/* 0x0623: memx_exec_next */ 0x9802b2b9, 0x10b60013, - 0x10349504, + 0xf034e704, + 0xe033e701, + 0x0132b601, 0x980c30f0, 0x55f9de35, 0xf40612b8, - 0xd0fcec1e, + 0x0b98e41e, + 0xef0c98ee, + 0xf102cbbb, + 0xcf07c4b7, + 0xd0fc00bb, 0x21f5e0fc, - 0x00f8026b, -/* 0x049c: memx_info */ - 0x03acc7f1, + 0x00f802f1, +/* 0x065c: memx_info */ + 0x03c0c7f1, 0x0800b7f1, - 0x026b21f5, -/* 0x04aa: memx_recv */ + 0x02f121f5, +/* 0x066a: memx_recv */ 0xd6b000f8, - 0xc40bf401, + 0xac0bf401, 0xf400d6b0, 0x00f8e90b, -/* 0x04b8: memx_init */ -/* 0x04ba: perf_recv */ +/* 0x0678: memx_init */ +/* 0x067a: perf_recv */ 0x00f800f8, -/* 0x04bc: perf_init */ -/* 0x04be: i2c_drive_scl */ +/* 0x067c: perf_init */ +/* 0x067e: i2c_drive_scl */ 0x36b000f8, 0x0e0bf400, 0x07e007f1, 0xbd0001d0, -/* 0x04cf: i2c_drive_scl_lo */ +/* 0x068f: i2c_drive_scl_lo */ 0xf100f804, 0xd007e407, 0x04bd0001, -/* 0x04da: i2c_drive_sda */ +/* 0x069a: i2c_drive_sda */ 0x36b000f8, 0x0e0bf400, 0x07e007f1, 0xbd0002d0, -/* 0x04eb: i2c_drive_sda_lo */ +/* 0x06ab: i2c_drive_sda_lo */ 0xf100f804, 0xd007e407, 0x04bd0002, -/* 0x04f6: i2c_sense_scl */ +/* 0x06b6: i2c_sense_scl */ 0x32f400f8, 0xc437f101, 0x0033cf07, 0xf40431fd, 0x31f4060b, -/* 0x0509: i2c_sense_scl_done */ -/* 0x050b: i2c_sense_sda */ +/* 0x06c9: i2c_sense_scl_done */ +/* 0x06cb: i2c_sense_sda */ 0xf400f801, 0x37f10132, 0x33cf07c4, 0x0432fd00, 0xf4060bf4, -/* 0x051e: i2c_sense_sda_done */ +/* 0x06de: i2c_sense_sda_done */ 0x00f80131, -/* 0x0520: i2c_raise_scl */ +/* 0x06e0: i2c_raise_scl */ 0x47f140f9, 0x37f00898, - 0xbe21f501, -/* 0x052d: i2c_raise_scl_wait */ - 0xe8e7f104, + 0x7e21f501, +/* 0x06ed: i2c_raise_scl_wait */ + 0xe8e7f106, 0x6721f403, - 0x04f621f5, + 0x06b621f5, 0xb60901f4, 0x1bf40142, -/* 0x0541: i2c_raise_scl_done */ +/* 0x0701: i2c_raise_scl_done */ 0xf840fcef, -/* 0x0545: i2c_start */ - 0xf621f500, - 0x0d11f404, - 0x050b21f5, +/* 0x0705: i2c_start */ + 0xb621f500, + 0x0d11f406, + 0x06cb21f5, 0xf40611f4, -/* 0x0556: i2c_start_rep */ +/* 0x0716: i2c_start_rep */ 0x37f0300e, - 0xbe21f500, - 0x0137f004, - 0x04da21f5, + 0x7e21f500, + 0x0137f006, + 0x069a21f5, 0xb60076bb, 0x50f90465, 0xbb046594, 0x50bd0256, 0xfc0475fd, - 0x2021f550, - 0x0464b605, -/* 0x0583: i2c_start_send */ + 0xe021f550, + 0x0464b606, +/* 0x0743: i2c_start_send */ 0xf01f11f4, 0x21f50037, - 0xe7f104da, + 0xe7f1069a, 0x21f41388, 0x0037f067, - 0x04be21f5, + 0x067e21f5, 0x1388e7f1, -/* 0x059f: i2c_start_out */ +/* 0x075f: i2c_start_out */ 0xf86721f4, -/* 0x05a1: i2c_stop */ +/* 0x0761: i2c_stop */ 0x0037f000, - 0x04be21f5, + 0x067e21f5, 0xf50037f0, - 0xf104da21, + 0xf1069a21, 0xf403e8e7, 0x37f06721, - 0xbe21f501, - 0x88e7f104, + 0x7e21f501, + 0x88e7f106, 0x6721f413, 0xf50137f0, - 0xf104da21, + 0xf1069a21, 0xf41388e7, 0x00f86721, -/* 0x05d4: i2c_bitw */ - 0x04da21f5, +/* 0x0794: i2c_bitw */ + 0x069a21f5, 0x03e8e7f1, 0xbb6721f4, 0x65b60076, @@ -1234,18 +1419,18 @@ uint32_t nvd0_pwr_code[] = { 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x052021f5, + 0x06e021f5, 0xf40464b6, 0xe7f11811, 0x21f41388, 0x0037f067, - 0x04be21f5, + 0x067e21f5, 0x1388e7f1, -/* 0x0613: i2c_bitw_out */ +/* 0x07d3: i2c_bitw_out */ 0xf86721f4, -/* 0x0615: i2c_bitr */ +/* 0x07d5: i2c_bitr */ 0x0137f000, - 0x04da21f5, + 0x069a21f5, 0x03e8e7f1, 0xbb6721f4, 0x65b60076, @@ -1253,19 +1438,19 @@ uint32_t nvd0_pwr_code[] = { 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x052021f5, + 0x06e021f5, 0xf40464b6, 0x21f51b11, - 0x37f0050b, - 0xbe21f500, - 0x88e7f104, + 0x37f006cb, + 0x7e21f500, + 0x88e7f106, 0x6721f413, 0xf4013cf0, -/* 0x065a: i2c_bitr_done */ +/* 0x081a: i2c_bitr_done */ 0x00f80131, -/* 0x065c: i2c_get_byte */ +/* 0x081c: i2c_get_byte */ 0xf00057f0, -/* 0x0662: i2c_get_byte_next */ +/* 0x0822: i2c_get_byte_next */ 0x54b60847, 0x0076bb01, 0xf90465b6, @@ -1273,7 +1458,7 @@ uint32_t nvd0_pwr_code[] = { 0xbd0256bb, 0x0475fd50, 0x21f550fc, - 0x64b60615, + 0x64b607d5, 0x2b11f404, 0xb60553fd, 0x1bf40142, @@ -1283,12 +1468,12 @@ uint32_t nvd0_pwr_code[] = { 0xbb046594, 0x50bd0256, 0xfc0475fd, - 0xd421f550, - 0x0464b605, -/* 0x06ac: i2c_get_byte_done */ -/* 0x06ae: i2c_put_byte */ + 0x9421f550, + 0x0464b607, +/* 0x086c: i2c_get_byte_done */ +/* 0x086e: i2c_put_byte */ 0x47f000f8, -/* 0x06b1: i2c_put_byte_next */ +/* 0x0871: i2c_put_byte_next */ 0x0142b608, 0xbb3854ff, 0x65b60076, @@ -1296,7 +1481,7 @@ uint32_t nvd0_pwr_code[] = { 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x05d421f5, + 0x079421f5, 0xf40464b6, 0x46b03411, 0xd81bf400, @@ -1305,21 +1490,21 @@ uint32_t nvd0_pwr_code[] = { 0xbb046594, 0x50bd0256, 0xfc0475fd, - 0x1521f550, - 0x0464b606, + 0xd521f550, + 0x0464b607, 0xbb0f11f4, 0x36b00076, 0x061bf401, -/* 0x0707: i2c_put_byte_done */ +/* 0x08c7: i2c_put_byte_done */ 0xf80132f4, -/* 0x0709: i2c_addr */ +/* 0x08c9: i2c_addr */ 0x0076bb00, 0xf90465b6, 0x04659450, 0xbd0256bb, 0x0475fd50, 0x21f550fc, - 0x64b60545, + 0x64b60705, 0x2911f404, 0x012ec3e7, 0xfd0134b6, @@ -1329,30 +1514,30 @@ uint32_t nvd0_pwr_code[] = { 0x0256bb04, 0x75fd50bd, 0xf550fc04, - 0xb606ae21, -/* 0x074e: i2c_addr_done */ + 0xb6086e21, +/* 0x090e: i2c_addr_done */ 0x00f80464, -/* 0x0750: i2c_acquire_addr */ +/* 0x0910: i2c_acquire_addr */ 0xb6f8cec7, 0xe0b705e4, 0x00f8d014, -/* 0x075c: i2c_acquire */ - 0x075021f5, +/* 0x091c: i2c_acquire */ + 0x091021f5, 0xf00421f4, 0x21f403d9, -/* 0x076b: i2c_release */ +/* 0x092b: i2c_release */ 0xf500f833, - 0xf4075021, + 0xf4091021, 0xdaf00421, 0x3321f403, -/* 0x077a: i2c_recv */ +/* 0x093a: i2c_recv */ 0x32f400f8, 0xf8c1c701, 0xb00214b6, 0x1ff52816, 0x13a0013a, - 0x32980bd4, - 0xac13a000, + 0x32980be8, + 0xc013a000, 0x0031980b, 0xf90231f4, 0xf9e0f9d0, @@ -1364,8 +1549,8 @@ uint32_t nvd0_pwr_code[] = { 0xbb046594, 0x50bd0256, 0xfc0475fd, - 0x5c21f550, - 0x0464b607, + 0x1c21f550, + 0x0464b609, 0xd6b0d0fc, 0xb31bf500, 0x0057f000, @@ -1374,8 +1559,8 @@ uint32_t nvd0_pwr_code[] = { 0xbb046594, 0x50bd0256, 0xfc0475fd, - 0x0921f550, - 0x0464b607, + 0xc921f550, + 0x0464b608, 0x00d011f5, 0xbbe0c5c7, 0x65b60076, @@ -1383,7 +1568,7 @@ uint32_t nvd0_pwr_code[] = { 0x56bb0465, 0xfd50bd02, 0x50fc0475, - 0x06ae21f5, + 0x086e21f5, 0xf50464b6, 0xf000ad11, 0x76bb0157, @@ -1392,7 +1577,7 @@ uint32_t nvd0_pwr_code[] = { 0x0256bb04, 0x75fd50bd, 0xf550fc04, - 0xb6070921, + 0xb608c921, 0x11f50464, 0x76bb008a, 0x0465b600, @@ -1400,7 +1585,7 @@ uint32_t nvd0_pwr_code[] = { 0x0256bb04, 0x75fd50bd, 0xf550fc04, - 0xb6065c21, + 0xb6081c21, 0x11f40464, 0xe05bcb6a, 0xb60076bb, @@ -1408,38 +1593,38 @@ uint32_t nvd0_pwr_code[] = { 0xbb046594, 0x50bd0256, 0xfc0475fd, - 0xa121f550, - 0x0464b605, + 0x6121f550, + 0x0464b607, 0xbd025bb9, 0x430ef474, -/* 0x0880: i2c_recv_not_rd08 */ +/* 0x0a40: i2c_recv_not_rd08 */ 0xf401d6b0, 0x57f03d1b, - 0x0921f500, - 0x3311f407, + 0xc921f500, + 0x3311f408, 0xf5e0c5c7, - 0xf406ae21, + 0xf4086e21, 0x57f02911, - 0x0921f500, - 0x1f11f407, + 0xc921f500, + 0x1f11f408, 0xf5e0b5c7, - 0xf406ae21, + 0xf4086e21, 0x21f51511, - 0x74bd05a1, + 0x74bd0761, 0xf408c5c7, 0x32f4091b, 0x030ef402, -/* 0x08c0: i2c_recv_not_wr08 */ -/* 0x08c0: i2c_recv_done */ +/* 0x0a80: i2c_recv_not_wr08 */ +/* 0x0a80: i2c_recv_done */ 0xf5f8cec7, - 0xfc076b21, + 0xfc092b21, 0xf4d0fce0, 0x7cb90a12, - 0x6b21f502, -/* 0x08d5: i2c_recv_exit */ -/* 0x08d7: i2c_init */ + 0xf121f502, +/* 0x0a95: i2c_recv_exit */ +/* 0x0a97: i2c_init */ 0xf800f802, -/* 0x08d9: test_recv */ +/* 0x0a99: test_recv */ 0xd817f100, 0x0011cf05, 0xf10110b6, @@ -1447,29 +1632,29 @@ uint32_t nvd0_pwr_code[] = { 0x04bd0001, 0xd900e7f1, 0x134fe3f1, - 0x01b621f5, -/* 0x08fa: test_init */ + 0x022321f5, +/* 0x0aba: test_init */ 0xe7f100f8, 0x21f50800, - 0x00f801b6, -/* 0x0904: idle_recv */ -/* 0x0906: idle */ + 0x00f80223, +/* 0x0ac4: idle_recv */ +/* 0x0ac6: idle */ 0x31f400f8, 0xd417f100, 0x0011cf05, 0xf10110b6, 0xd005d407, 0x04bd0001, -/* 0x091c: idle_loop */ +/* 0x0adc: idle_loop */ 0xf45817f0, -/* 0x0922: idle_proc */ -/* 0x0922: idle_proc_exec */ +/* 0x0ae2: idle_proc */ +/* 0x0ae2: idle_proc_exec */ 0x10f90232, 0xf5021eb9, - 0xfc027421, + 0xfc02fa21, 0x0911f410, 0xf40231f4, -/* 0x0936: idle_proc_next */ +/* 0x0af6: idle_proc_next */ 0x10b6ef0e, 0x061fb858, 0xf4e61bf4, @@ -1521,4 +1706,20 @@ uint32_t nvd0_pwr_code[] = { 0x00000000, 0x00000000, 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/os.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/os.h index 574acfa44c8c78f327dcd9d59cc7e1b6e9b2bc1f..522e3079f82428e401030c8d34c7124865ec7a3d 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/os.h +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/os.h @@ -19,11 +19,12 @@ #define MEMX_MSG_EXEC 1 /* MEMX: script opcode definitions */ -#define MEMX_ENTER 0 -#define MEMX_LEAVE 1 -#define MEMX_WR32 2 -#define MEMX_WAIT 3 -#define MEMX_DELAY 4 +#define MEMX_ENTER 1 +#define MEMX_LEAVE 2 +#define MEMX_WR32 3 +#define MEMX_WAIT 4 +#define MEMX_DELAY 5 +#define MEMX_VBLANK 6 /* I2C_: message identifiers */ #define I2C__MSG_RD08 0 diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/memx.c b/drivers/gpu/drm/nouveau/core/subdev/pwr/memx.c index def6a9ac68cfa5b6db7c52897df5e4af19c3a5c2..65eaa2546cad2ce5abbcbc32eea9ac008c8bfd2f 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/pwr/memx.c +++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/memx.c @@ -20,10 +20,11 @@ memx_out(struct nouveau_memx *memx) struct nouveau_pwr *ppwr = memx->ppwr; int i; - if (memx->c.size) { + if (memx->c.mthd) { nv_wr32(ppwr, 0x10a1c4, (memx->c.size << 16) | memx->c.mthd); for (i = 0; i < memx->c.size; i++) nv_wr32(ppwr, 0x10a1c4, memx->c.data[i]); + memx->c.mthd = 0; memx->c.size = 0; } } @@ -32,7 +33,7 @@ static void memx_cmd(struct nouveau_memx *memx, u32 mthd, u32 size, u32 data[]) { if ((memx->c.size + size >= ARRAY_SIZE(memx->c.data)) || - (memx->c.size && memx->c.mthd != mthd)) + (memx->c.mthd && memx->c.mthd != mthd)) memx_out(memx); memcpy(&memx->c.data[memx->c.size], data, size * sizeof(data[0])); memx->c.size += size; @@ -62,8 +63,7 @@ nouveau_memx_init(struct nouveau_pwr *ppwr, struct nouveau_memx **pmemx) nv_wr32(ppwr, 0x10a580, 0x00000003); } while (nv_rd32(ppwr, 0x10a580) != 0x00000003); nv_wr32(ppwr, 0x10a1c0, 0x01000000 | memx->base); - nv_wr32(ppwr, 0x10a1c4, 0x00010000 | MEMX_ENTER); - nv_wr32(ppwr, 0x10a1c4, 0x00000000); + return 0; } @@ -78,7 +78,6 @@ nouveau_memx_fini(struct nouveau_memx **pmemx, bool exec) memx_out(memx); /* release data segment access */ - nv_wr32(ppwr, 0x10a1c4, 0x00000000 | MEMX_LEAVE); finish = nv_rd32(ppwr, 0x10a1c0) & 0x00ffffff; nv_wr32(ppwr, 0x10a580, 0x00000000); @@ -88,6 +87,8 @@ nouveau_memx_fini(struct nouveau_memx **pmemx, bool exec) memx->base, finish); } + nv_debug(memx->ppwr, "Exec took %uns, PPWR_IN %08x\n", + reply[0], reply[1]); kfree(memx); return 0; } @@ -117,4 +118,51 @@ nouveau_memx_nsec(struct nouveau_memx *memx, u32 nsec) memx_out(memx); /* fuc can't handle multiple */ } +void +nouveau_memx_wait_vblank(struct nouveau_memx *memx) +{ + struct nouveau_pwr *ppwr = memx->ppwr; + u32 heads, x, y, px = 0; + int i, head_sync; + + if (nv_device(ppwr)->chipset < 0xd0) { + heads = nv_rd32(ppwr, 0x610050); + for (i = 0; i < 2; i++) { + /* Heuristic: sync to head with biggest resolution */ + if (heads & (2 << (i << 3))) { + x = nv_rd32(ppwr, 0x610b40 + (0x540 * i)); + y = (x & 0xffff0000) >> 16; + x &= 0x0000ffff; + if ((x * y) > px) { + px = (x * y); + head_sync = i; + } + } + } + } + + if (px == 0) { + nv_debug(memx->ppwr, "WAIT VBLANK !NO ACTIVE HEAD\n"); + return; + } + + nv_debug(memx->ppwr, "WAIT VBLANK HEAD%d\n", head_sync); + memx_cmd(memx, MEMX_VBLANK, 1, (u32[]){ head_sync }); + memx_out(memx); /* fuc can't handle multiple */ +} + +void +nouveau_memx_block(struct nouveau_memx *memx) +{ + nv_debug(memx->ppwr, " HOST BLOCKED\n"); + memx_cmd(memx, MEMX_ENTER, 0, NULL); +} + +void +nouveau_memx_unblock(struct nouveau_memx *memx) +{ + nv_debug(memx->ppwr, " HOST UNBLOCKED\n"); + memx_cmd(memx, MEMX_LEAVE, 0, NULL); +} + #endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c index 016990a8252c682f7bc7c93ead9fa8a9c5394979..3656d605168fce2295d86f64d5eff0da2bc5dede 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c @@ -31,6 +31,8 @@ #include #include +#include + static int nouveau_fan_update(struct nouveau_fan *fan, bool immediate, int target) { @@ -275,8 +277,11 @@ nouveau_therm_fan_ctor(struct nouveau_therm *therm) /* other random init... */ nouveau_therm_fan_set_defaults(therm); nvbios_perf_fan_parse(bios, &priv->fan->perf); - if (nvbios_therm_fan_parse(bios, &priv->fan->bios)) - nv_error(therm, "parsing the thermal table failed\n"); + if (!nvbios_fan_parse(bios, &priv->fan->bios)) { + nv_debug(therm, "parsing the fan table failed\n"); + if (nvbios_therm_fan_parse(bios, &priv->fan->bios)) + nv_error(therm, "parsing both fan tables failed\n"); + } nouveau_therm_fan_safety_checks(therm); return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c index 9a5c07340263305f1310d1e6a3520808c2606049..c629d7f2a6a44025224e4123c5273b4705a2cf68 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c @@ -25,6 +25,8 @@ #include #include +#include +#include #include "priv.h" @@ -86,11 +88,15 @@ nouveau_fanpwm_create(struct nouveau_therm *therm, struct dcb_gpio_func *func) { struct nouveau_device *device = nv_device(therm); struct nouveau_therm_priv *tpriv = (void *)therm; + struct nouveau_bios *bios = nouveau_bios(therm); struct nouveau_fanpwm_priv *priv; + struct nvbios_therm_fan fan; u32 divs, duty; + nvbios_fan_parse(bios, &fan); + if (!nouveau_boolopt(device->cfgopt, "NvFanPWM", func->param) || - !therm->pwm_ctrl || + !therm->pwm_ctrl || fan.type == NVBIOS_THERM_FAN_TOGGLE || therm->pwm_get(therm, func->line, &divs, &duty) == -ENODEV) return -ENODEV; diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/gm107.c b/drivers/gpu/drm/nouveau/core/subdev/therm/gm107.c new file mode 100644 index 0000000000000000000000000000000000000000..668cf3322285a95897848fd971fb6d0414557863 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/gm107.c @@ -0,0 +1,93 @@ +/* + * Copyright 2014 Martin Peres + * + * 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. + * + * Authors: Martin Peres + */ + +#include "priv.h" + +struct gm107_therm_priv { + struct nouveau_therm_priv base; +}; + +static int +gm107_fan_pwm_ctrl(struct nouveau_therm *therm, int line, bool enable) +{ + /* nothing to do, it seems hardwired */ + return 0; +} + +static int +gm107_fan_pwm_get(struct nouveau_therm *therm, int line, u32 *divs, u32 *duty) +{ + *divs = nv_rd32(therm, 0x10eb20) & 0x1fff; + *duty = nv_rd32(therm, 0x10eb24) & 0x1fff; + return 0; +} + +static int +gm107_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty) +{ + nv_mask(therm, 0x10eb10, 0x1fff, divs); /* keep the high bits */ + nv_wr32(therm, 0x10eb14, duty | 0x80000000); + return 0; +} + +static int +gm107_fan_pwm_clock(struct nouveau_therm *therm, int line) +{ + return nv_device(therm)->crystal * 1000; +} + +static int +gm107_therm_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct gm107_therm_priv *priv; + int ret; + + ret = nouveau_therm_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.base.pwm_ctrl = gm107_fan_pwm_ctrl; + priv->base.base.pwm_get = gm107_fan_pwm_get; + priv->base.base.pwm_set = gm107_fan_pwm_set; + priv->base.base.pwm_clock = gm107_fan_pwm_clock; + priv->base.base.temp_get = nv84_temp_get; + priv->base.base.fan_sense = nva3_therm_fan_sense; + priv->base.sensor.program_alarms = nouveau_therm_program_alarms_polling; + return nouveau_therm_preinit(&priv->base.base); +} + +struct nouveau_oclass +gm107_therm_oclass = { + .handle = NV_SUBDEV(THERM, 0x117), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = gm107_therm_ctor, + .dtor = _nouveau_therm_dtor, + .init = nvd0_therm_init, + .fini = nv84_therm_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nv84.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nv84.c index 1d15c52fad0c3030e4a2c103a2a69b3529aa1b19..14e2e09bfc24ce8ae8cf05b554a7eae637d82f44 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/nv84.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nv84.c @@ -24,6 +24,7 @@ */ #include "priv.h" +#include struct nv84_therm_priv { struct nouveau_therm_priv base; @@ -32,7 +33,25 @@ struct nv84_therm_priv { int nv84_temp_get(struct nouveau_therm *therm) { - return nv_rd32(therm, 0x20400); + struct nouveau_fuse *fuse = nouveau_fuse(therm); + + if (nv_ro32(fuse, 0x1a8) == 1) + return nv_rd32(therm, 0x20400); + else + return -ENODEV; +} + +void +nv84_sensor_setup(struct nouveau_therm *therm) +{ + struct nouveau_fuse *fuse = nouveau_fuse(therm); + + /* enable temperature reading for cards with insane defaults */ + if (nv_ro32(fuse, 0x1a8) == 1) { + nv_mask(therm, 0x20008, 0x80008000, 0x80000000); + nv_mask(therm, 0x2000c, 0x80000003, 0x00000000); + mdelay(20); /* wait for the temperature to stabilize */ + } } static void @@ -170,6 +189,21 @@ nv84_therm_intr(struct nouveau_subdev *subdev) spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags); } +static int +nv84_therm_init(struct nouveau_object *object) +{ + struct nv84_therm_priv *priv = (void *)object; + int ret; + + ret = nouveau_therm_init(&priv->base.base); + if (ret) + return ret; + + nv84_sensor_setup(&priv->base.base); + + return 0; +} + static int nv84_therm_ctor(struct nouveau_object *parent, struct nouveau_object *engine, @@ -228,7 +262,7 @@ nv84_therm_oclass = { .ofuncs = &(struct nouveau_ofuncs) { .ctor = nv84_therm_ctor, .dtor = _nouveau_therm_dtor, - .init = _nouveau_therm_init, + .init = nv84_therm_init, .fini = nv84_therm_fini, }, }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c index 0478b2e3fb1de76ee34e40a56e0d6209ffff1d60..7893357a7e9f474c1d777527933406a27c4f3330 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c @@ -51,6 +51,8 @@ nva3_therm_init(struct nouveau_object *object) if (ret) return ret; + nv84_sensor_setup(&priv->base.base); + /* enable fan tach, count revolutions per-second */ nv_mask(priv, 0x00e720, 0x00000003, 0x00000002); if (tach->func != DCB_GPIO_UNUSED) { diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c index bbf117be572f4617cff0fd06907fb2a13977ae93..b70f7cc649b87a8973456ca4a5f4f3bd28f4f3b6 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c @@ -114,7 +114,7 @@ nvd0_fan_pwm_clock(struct nouveau_therm *therm, int line) return nv_device(therm)->crystal * 1000 / 10; } -static int +int nvd0_therm_init(struct nouveau_object *object) { struct nvd0_therm_priv *priv = (void *)object; @@ -150,6 +150,8 @@ nvd0_therm_ctor(struct nouveau_object *parent, if (ret) return ret; + nv84_sensor_setup(&priv->base.base); + priv->base.base.pwm_ctrl = nvd0_fan_pwm_ctrl; priv->base.base.pwm_get = nvd0_fan_pwm_get; priv->base.base.pwm_set = nvd0_fan_pwm_set; diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h index 916fca5c78162488a23e65e11188cda9b2d0e0eb..7dba8c281a0b3fe8a19ec2d93bb38c104f811ec7 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h @@ -145,10 +145,13 @@ int nv50_fan_pwm_get(struct nouveau_therm *, int, u32 *, u32 *); int nv50_fan_pwm_set(struct nouveau_therm *, int, u32, u32); int nv50_fan_pwm_clock(struct nouveau_therm *, int); int nv84_temp_get(struct nouveau_therm *therm); +void nv84_sensor_setup(struct nouveau_therm *therm); int nv84_therm_fini(struct nouveau_object *object, bool suspend); int nva3_therm_fan_sense(struct nouveau_therm *); +int nvd0_therm_init(struct nouveau_object *object); + int nouveau_fanpwm_create(struct nouveau_therm *, struct dcb_gpio_func *); int nouveau_fantog_create(struct nouveau_therm *, struct dcb_gpio_func *); int nouveau_fannil_create(struct nouveau_therm *); diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/base.c b/drivers/gpu/drm/nouveau/core/subdev/vm/base.c index 7dd680ff2f6f63683f34e39a1d7821f3c73c140c..f75a683bd47a6ddf38e3ec13260f657a8c525c65 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/vm/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/vm/base.c @@ -296,7 +296,7 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift, int ret; mutex_lock(&nv_subdev(vmm)->mutex); - ret = nouveau_mm_head(&vm->mm, page_shift, msize, msize, align, + ret = nouveau_mm_head(&vm->mm, 0, page_shift, msize, msize, align, &vma->node); if (unlikely(ret != 0)) { mutex_unlock(&nv_subdev(vmm)->mutex); diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c index b90aa5c1f90a1790cc5a2b83cdc40f006a6663b9..fca6a1f9c20c5f9957f8f66a1a399a73d54f3873 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -1127,7 +1127,7 @@ nv04_crtc_create(struct drm_device *dev, int crtc_num) drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256); ret = nouveau_bo_new(dev, 64*64*4, 0x100, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, &nv_crtc->cursor.nvbo); + 0, 0x0000, NULL, NULL, &nv_crtc->cursor.nvbo); if (!ret) { ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); if (!ret) { diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c index b36afcbbc83febc9ccdee1cc63442b1a3ed8a46c..1e9056a8df945917750e9d7effb576e69a9351d9 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c +++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c @@ -97,7 +97,8 @@ nv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, uint32_t src_w, uint32_t src_h) { struct nvif_device *dev = &nouveau_drm(plane->dev)->device; - struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane; + struct nouveau_plane *nv_plane = + container_of(plane, struct nouveau_plane, base); struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); struct nouveau_bo *cur = nv_plane->cur; @@ -173,7 +174,8 @@ static int nv10_disable_plane(struct drm_plane *plane) { struct nvif_device *dev = &nouveau_drm(plane->dev)->device; - struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane; + struct nouveau_plane *nv_plane = + container_of(plane, struct nouveau_plane, base); nvif_wr32(dev, NV_PVIDEO_STOP, 1); if (nv_plane->cur) { @@ -224,7 +226,8 @@ nv_set_property(struct drm_plane *plane, struct drm_property *property, uint64_t value) { - struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane; + struct nouveau_plane *nv_plane = + container_of(plane, struct nouveau_plane, base); if (property == nv_plane->props.colorkey) nv_plane->colorkey = value; @@ -344,7 +347,8 @@ nv04_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, uint32_t src_w, uint32_t src_h) { struct nvif_device *dev = &nouveau_drm(plane->dev)->device; - struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane; + struct nouveau_plane *nv_plane = + container_of(plane, struct nouveau_plane, base); struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); struct nouveau_bo *cur = nv_plane->cur; uint32_t overlay = 1; @@ -423,7 +427,8 @@ static int nv04_disable_plane(struct drm_plane *plane) { struct nvif_device *dev = &nouveau_drm(plane->dev)->device; - struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane; + struct nouveau_plane *nv_plane = + container_of(plane, struct nouveau_plane, base); nvif_mask(dev, NV_PVIDEO_OVERLAY, 1, 0); nvif_wr32(dev, NV_PVIDEO_OE_STATE, 0); diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c index 615714c1727d6b6870b3ec2698bc204c74ab196a..a24faa5e2a2ae67a5075cb2f9506b9f93fa66083 100644 --- a/drivers/gpu/drm/nouveau/nouveau_abi16.c +++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c @@ -448,7 +448,7 @@ nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS) list_add(&ntfy->head, &chan->notifiers); ntfy->handle = info->handle; - ret = nouveau_mm_head(&chan->heap, 1, info->size, info->size, 1, + ret = nouveau_mm_head(&chan->heap, 0, 1, info->size, info->size, 1, &ntfy->node); if (ret) goto done; diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 01da508625f27e4ad1c27393b8eaa928634564aa..3d474ac03f8847c7985c2c664a88bed1cfa189b0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -88,13 +88,13 @@ nv10_bo_get_tile_region(struct drm_device *dev, int i) static void nv10_bo_put_tile_region(struct drm_device *dev, struct nouveau_drm_tile *tile, - struct nouveau_fence *fence) + struct fence *fence) { struct nouveau_drm *drm = nouveau_drm(dev); if (tile) { spin_lock(&drm->tile.lock); - tile->fence = nouveau_fence_ref(fence); + tile->fence = (struct nouveau_fence *)fence_get(fence); tile->used = false; spin_unlock(&drm->tile.lock); } @@ -181,7 +181,7 @@ nouveau_bo_fixup_align(struct nouveau_bo *nvbo, u32 flags, int nouveau_bo_new(struct drm_device *dev, int size, int align, uint32_t flags, uint32_t tile_mode, uint32_t tile_flags, - struct sg_table *sg, + struct sg_table *sg, struct reservation_object *robj, struct nouveau_bo **pnvbo) { struct nouveau_drm *drm = nouveau_drm(dev); @@ -230,7 +230,7 @@ nouveau_bo_new(struct drm_device *dev, int size, int align, ret = ttm_bo_init(&drm->ttm.bdev, &nvbo->bo, size, type, &nvbo->placement, align >> PAGE_SHIFT, false, NULL, acc_size, sg, - nouveau_bo_del_ttm); + robj, nouveau_bo_del_ttm); if (ret) { /* ttm will call nouveau_bo_del_ttm if it fails.. */ return ret; @@ -241,16 +241,16 @@ nouveau_bo_new(struct drm_device *dev, int size, int align, } static void -set_placement_list(uint32_t *pl, unsigned *n, uint32_t type, uint32_t flags) +set_placement_list(struct ttm_place *pl, unsigned *n, uint32_t type, uint32_t flags) { *n = 0; if (type & TTM_PL_FLAG_VRAM) - pl[(*n)++] = TTM_PL_FLAG_VRAM | flags; + pl[(*n)++].flags = TTM_PL_FLAG_VRAM | flags; if (type & TTM_PL_FLAG_TT) - pl[(*n)++] = TTM_PL_FLAG_TT | flags; + pl[(*n)++].flags = TTM_PL_FLAG_TT | flags; if (type & TTM_PL_FLAG_SYSTEM) - pl[(*n)++] = TTM_PL_FLAG_SYSTEM | flags; + pl[(*n)++].flags = TTM_PL_FLAG_SYSTEM | flags; } static void @@ -258,6 +258,7 @@ set_placement_range(struct nouveau_bo *nvbo, uint32_t type) { struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); u32 vram_pages = drm->device.info.ram_size >> PAGE_SHIFT; + unsigned i, fpfn, lpfn; if (drm->device.info.family == NV_DEVICE_INFO_V0_CELSIUS && nvbo->tile_mode && (type & TTM_PL_FLAG_VRAM) && @@ -269,11 +270,19 @@ set_placement_range(struct nouveau_bo *nvbo, uint32_t type) * at the same time. */ if (nvbo->tile_flags & NOUVEAU_GEM_TILE_ZETA) { - nvbo->placement.fpfn = vram_pages / 2; - nvbo->placement.lpfn = ~0; + fpfn = vram_pages / 2; + lpfn = ~0; } else { - nvbo->placement.fpfn = 0; - nvbo->placement.lpfn = vram_pages / 2; + fpfn = 0; + lpfn = vram_pages / 2; + } + for (i = 0; i < nvbo->placement.num_placement; ++i) { + nvbo->placements[i].fpfn = fpfn; + nvbo->placements[i].lpfn = lpfn; + } + for (i = 0; i < nvbo->placement.num_busy_placement; ++i) { + nvbo->busy_placements[i].fpfn = fpfn; + nvbo->busy_placements[i].lpfn = lpfn; } } } @@ -961,13 +970,14 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr, } mutex_lock_nested(&cli->mutex, SINGLE_DEPTH_NESTING); - ret = nouveau_fence_sync(bo->sync_obj, chan); + ret = nouveau_fence_sync(nouveau_bo(bo), chan, true, intr); if (ret == 0) { ret = drm->ttm.move(chan, bo, &bo->mem, new_mem); if (ret == 0) { ret = nouveau_fence_new(chan, false, &fence); if (ret == 0) { - ret = ttm_bo_move_accel_cleanup(bo, fence, + ret = ttm_bo_move_accel_cleanup(bo, + &fence->base, evict, no_wait_gpu, new_mem); @@ -1041,12 +1051,15 @@ static int nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr, bool no_wait_gpu, struct ttm_mem_reg *new_mem) { - u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING; + struct ttm_place placement_memtype = { + .fpfn = 0, + .lpfn = 0, + .flags = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING + }; struct ttm_placement placement; struct ttm_mem_reg tmp_mem; int ret; - placement.fpfn = placement.lpfn = 0; placement.num_placement = placement.num_busy_placement = 1; placement.placement = placement.busy_placement = &placement_memtype; @@ -1074,12 +1087,15 @@ static int nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr, bool no_wait_gpu, struct ttm_mem_reg *new_mem) { - u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING; + struct ttm_place placement_memtype = { + .fpfn = 0, + .lpfn = 0, + .flags = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING + }; struct ttm_placement placement; struct ttm_mem_reg tmp_mem; int ret; - placement.fpfn = placement.lpfn = 0; placement.num_placement = placement.num_busy_placement = 1; placement.placement = placement.busy_placement = &placement_memtype; @@ -1152,8 +1168,9 @@ nouveau_bo_vm_cleanup(struct ttm_buffer_object *bo, { struct nouveau_drm *drm = nouveau_bdev(bo->bdev); struct drm_device *dev = drm->dev; + struct fence *fence = reservation_object_get_excl(bo->resv); - nv10_bo_put_tile_region(dev, *old_tile, bo->sync_obj); + nv10_bo_put_tile_region(dev, *old_tile, fence); *old_tile = new_tile; } @@ -1197,9 +1214,7 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr, } /* Fallback to software copy. */ - spin_lock(&bo->bdev->fence_lock); ret = ttm_bo_wait(bo, true, intr, no_wait_gpu); - spin_unlock(&bo->bdev->fence_lock); if (ret == 0) ret = ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem); @@ -1294,7 +1309,7 @@ nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) struct nouveau_bo *nvbo = nouveau_bo(bo); struct nvif_device *device = &drm->device; u32 mappable = nv_device_resource_len(nvkm_device(device), 1) >> PAGE_SHIFT; - int ret; + int i, ret; /* as long as the bo isn't in vram, and isn't tiled, we've got * nothing to do here. @@ -1319,9 +1334,16 @@ nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) bo->mem.start + bo->mem.num_pages < mappable) return 0; + for (i = 0; i < nvbo->placement.num_placement; ++i) { + nvbo->placements[i].fpfn = 0; + nvbo->placements[i].lpfn = mappable; + } + + for (i = 0; i < nvbo->placement.num_busy_placement; ++i) { + nvbo->busy_placements[i].fpfn = 0; + nvbo->busy_placements[i].lpfn = mappable; + } - nvbo->placement.fpfn = 0; - nvbo->placement.lpfn = mappable; nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_VRAM, 0); return nouveau_bo_validate(nvbo, false, false); } @@ -1436,47 +1458,14 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm) } void -nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence) -{ - struct nouveau_fence *new_fence = nouveau_fence_ref(fence); - struct nouveau_fence *old_fence = NULL; - - spin_lock(&nvbo->bo.bdev->fence_lock); - old_fence = nvbo->bo.sync_obj; - nvbo->bo.sync_obj = new_fence; - spin_unlock(&nvbo->bo.bdev->fence_lock); - - nouveau_fence_unref(&old_fence); -} - -static void -nouveau_bo_fence_unref(void **sync_obj) -{ - nouveau_fence_unref((struct nouveau_fence **)sync_obj); -} - -static void * -nouveau_bo_fence_ref(void *sync_obj) +nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence, bool exclusive) { - return nouveau_fence_ref(sync_obj); -} + struct reservation_object *resv = nvbo->bo.resv; -static bool -nouveau_bo_fence_signalled(void *sync_obj) -{ - return nouveau_fence_done(sync_obj); -} - -static int -nouveau_bo_fence_wait(void *sync_obj, bool lazy, bool intr) -{ - return nouveau_fence_wait(sync_obj, lazy, intr); -} - -static int -nouveau_bo_fence_flush(void *sync_obj) -{ - return 0; + if (exclusive) + reservation_object_add_excl_fence(resv, &fence->base); + else if (fence) + reservation_object_add_shared_fence(resv, &fence->base); } struct ttm_bo_driver nouveau_bo_driver = { @@ -1489,11 +1478,6 @@ struct ttm_bo_driver nouveau_bo_driver = { .move_notify = nouveau_bo_move_ntfy, .move = nouveau_bo_move, .verify_access = nouveau_bo_verify_access, - .sync_obj_signaled = nouveau_bo_fence_signalled, - .sync_obj_wait = nouveau_bo_fence_wait, - .sync_obj_flush = nouveau_bo_fence_flush, - .sync_obj_unref = nouveau_bo_fence_unref, - .sync_obj_ref = nouveau_bo_fence_ref, .fault_reserve_notify = &nouveau_ttm_fault_reserve_notify, .io_mem_reserve = &nouveau_ttm_io_mem_reserve, .io_mem_free = &nouveau_ttm_io_mem_free, diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h index ff17c1f432fc9e9312ace42491385bb76226b381..22d2c764d80bd17c9fe755abc56b225722eee513 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.h +++ b/drivers/gpu/drm/nouveau/nouveau_bo.h @@ -1,6 +1,8 @@ #ifndef __NOUVEAU_BO_H__ #define __NOUVEAU_BO_H__ +#include + struct nouveau_channel; struct nouveau_fence; struct nouveau_vma; @@ -9,8 +11,8 @@ struct nouveau_bo { struct ttm_buffer_object bo; struct ttm_placement placement; u32 valid_domains; - u32 placements[3]; - u32 busy_placements[3]; + struct ttm_place placements[3]; + struct ttm_place busy_placements[3]; struct ttm_bo_kmap_obj kmap; struct list_head head; @@ -68,6 +70,7 @@ extern struct ttm_bo_driver nouveau_bo_driver; void nouveau_bo_move_init(struct nouveau_drm *); int nouveau_bo_new(struct drm_device *, int size, int align, u32 flags, u32 tile_mode, u32 tile_flags, struct sg_table *sg, + struct reservation_object *robj, struct nouveau_bo **); int nouveau_bo_pin(struct nouveau_bo *, u32 flags); int nouveau_bo_unpin(struct nouveau_bo *); @@ -78,7 +81,7 @@ u16 nouveau_bo_rd16(struct nouveau_bo *, unsigned index); void nouveau_bo_wr16(struct nouveau_bo *, unsigned index, u16 val); u32 nouveau_bo_rd32(struct nouveau_bo *, unsigned index); void nouveau_bo_wr32(struct nouveau_bo *, unsigned index, u32 val); -void nouveau_bo_fence(struct nouveau_bo *, struct nouveau_fence *); +void nouveau_bo_fence(struct nouveau_bo *, struct nouveau_fence *, bool exclusive); int nouveau_bo_validate(struct nouveau_bo *, bool interruptible, bool no_wait_gpu); diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c index 3440fc999f2f3290b3557ea495a26afb65fda874..589dbb582da200c543b2ee04e9296d58e0d70e1f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_chan.c +++ b/drivers/gpu/drm/nouveau/nouveau_chan.c @@ -36,7 +36,7 @@ #include "nouveau_abi16.h" MODULE_PARM_DESC(vram_pushbuf, "Create DMA push buffers in VRAM"); -static int nouveau_vram_pushbuf; +int nouveau_vram_pushbuf; module_param_named(vram_pushbuf, nouveau_vram_pushbuf, int, 0400); int @@ -106,7 +106,7 @@ nouveau_channel_prep(struct nouveau_drm *drm, struct nvif_device *device, if (nouveau_vram_pushbuf) target = TTM_PL_FLAG_VRAM; - ret = nouveau_bo_new(drm->dev, size, 0, target, 0, 0, NULL, + ret = nouveau_bo_new(drm->dev, size, 0, target, 0, 0, NULL, NULL, &chan->push.buffer); if (ret == 0) { ret = nouveau_bo_pin(chan->push.buffer, target); diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.h b/drivers/gpu/drm/nouveau/nouveau_chan.h index 20163709d608c788707658000b51c019dbb4b7d5..8309c24ee6981f63e3dc5257235ce122e5840de5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_chan.h +++ b/drivers/gpu/drm/nouveau/nouveau_chan.h @@ -47,4 +47,6 @@ int nouveau_channel_new(struct nouveau_drm *, struct nvif_device *, void nouveau_channel_del(struct nouveau_channel **); int nouveau_channel_idle(struct nouveau_channel *); +extern int nouveau_vram_pushbuf; + #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 1ec44c83e91932ffe82237143ea93ca6b94546c4..c8ac9482cf2ed12de114636b004bdd66813bc4ba 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -45,15 +45,15 @@ #include MODULE_PARM_DESC(tv_disable, "Disable TV-out detection"); -static int nouveau_tv_disable = 0; +int nouveau_tv_disable = 0; module_param_named(tv_disable, nouveau_tv_disable, int, 0400); MODULE_PARM_DESC(ignorelid, "Ignore ACPI lid status"); -static int nouveau_ignorelid = 0; +int nouveau_ignorelid = 0; module_param_named(ignorelid, nouveau_ignorelid, int, 0400); MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (default: enabled)"); -static int nouveau_duallink = 1; +int nouveau_duallink = 1; module_param_named(duallink, nouveau_duallink, int, 0400); struct nouveau_encoder * diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index 68029d041dd2fd856e636fe6e6b48a0971c625c4..629a380c708555b26522987036b458a01fb2983f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -105,4 +105,8 @@ nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc) struct drm_connector * nouveau_connector_create(struct drm_device *, int index); +extern int nouveau_tv_disable; +extern int nouveau_ignorelid; +extern int nouveau_duallink; + #endif /* __NOUVEAU_CONNECTOR_H__ */ diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 4a21b2b06ce29beece2bcd102d30f005fcbc1307..a88e6927f5713e29a0db95a769dd16401f9f920f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -126,7 +126,7 @@ nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos, if (etime) *etime = ns_to_ktime(args.scan.time[1]); if (*vpos < 0) - ret |= DRM_SCANOUTPOS_INVBL; + ret |= DRM_SCANOUTPOS_IN_VBLANK; return ret; } @@ -657,7 +657,7 @@ nouveau_page_flip_emit(struct nouveau_channel *chan, spin_unlock_irqrestore(&dev->event_lock, flags); /* Synchronize with the old framebuffer */ - ret = nouveau_fence_sync(old_bo->bo.sync_obj, chan); + ret = nouveau_fence_sync(old_bo, chan, false, false); if (ret) goto fail; @@ -716,19 +716,24 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, } mutex_lock(&cli->mutex); - - /* synchronise rendering channel with the kernel's channel */ - spin_lock(&new_bo->bo.bdev->fence_lock); - fence = nouveau_fence_ref(new_bo->bo.sync_obj); - spin_unlock(&new_bo->bo.bdev->fence_lock); - ret = nouveau_fence_sync(fence, chan); - nouveau_fence_unref(&fence); + ret = ttm_bo_reserve(&new_bo->bo, true, false, false, NULL); if (ret) goto fail_unpin; - ret = ttm_bo_reserve(&old_bo->bo, true, false, false, NULL); - if (ret) + /* synchronise rendering channel with the kernel's channel */ + ret = nouveau_fence_sync(new_bo, chan, false, true); + if (ret) { + ttm_bo_unreserve(&new_bo->bo); goto fail_unpin; + } + + if (new_bo != old_bo) { + ttm_bo_unreserve(&new_bo->bo); + + ret = ttm_bo_reserve(&old_bo->bo, true, false, false, NULL); + if (ret) + goto fail_unpin; + } /* Initialize a page flip struct */ *s = (struct nouveau_page_flip_state) @@ -774,7 +779,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, /* Update the crtc struct and cleanup */ crtc->primary->fb = fb; - nouveau_bo_fence(old_bo, fence); + nouveau_bo_fence(old_bo, fence, false); ttm_bo_unreserve(&old_bo->bo); if (old_bo != new_bo) nouveau_bo_unpin(old_bo); diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 3ed32dd9030364cd2a366726f9e6af09b05f21e3..57238076049f6fe16717ba6626bca954b1910aeb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -51,6 +51,7 @@ #include "nouveau_fence.h" #include "nouveau_debugfs.h" #include "nouveau_usif.h" +#include "nouveau_connector.h" MODULE_PARM_DESC(config, "option string to pass to driver core"); static char *nouveau_config; @@ -73,7 +74,9 @@ MODULE_PARM_DESC(runpm, "disable (0), force enable (1), optimus only default (-1 int nouveau_runtime_pm = -1; module_param_named(runpm, nouveau_runtime_pm, int, 0400); -static struct drm_driver driver; +static struct drm_driver driver_stub; +static struct drm_driver driver_pci; +static struct drm_driver driver_platform; static u64 nouveau_pci_name(struct pci_dev *pdev) @@ -322,7 +325,7 @@ static int nouveau_drm_probe(struct pci_dev *pdev, pci_set_master(pdev); - ret = drm_get_pci_dev(pdev, pent, &driver); + ret = drm_get_pci_dev(pdev, pent, &driver_pci); if (ret) { nouveau_object_ref(NULL, (struct nouveau_object **)&device); return ret; @@ -831,7 +834,7 @@ nouveau_driver_fops = { }; static struct drm_driver -driver = { +driver_stub = { .driver_features = DRIVER_USE_AGP | DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER, @@ -1002,6 +1005,23 @@ static int nouveau_pmops_runtime_idle(struct device *dev) return 1; } +static void nouveau_display_options(void) +{ + DRM_DEBUG_DRIVER("Loading Nouveau with parameters:\n"); + + DRM_DEBUG_DRIVER("... tv_disable : %d\n", nouveau_tv_disable); + DRM_DEBUG_DRIVER("... ignorelid : %d\n", nouveau_ignorelid); + DRM_DEBUG_DRIVER("... duallink : %d\n", nouveau_duallink); + DRM_DEBUG_DRIVER("... nofbaccel : %d\n", nouveau_nofbaccel); + DRM_DEBUG_DRIVER("... config : %s\n", nouveau_config); + DRM_DEBUG_DRIVER("... debug : %s\n", nouveau_debug); + DRM_DEBUG_DRIVER("... noaccel : %d\n", nouveau_noaccel); + DRM_DEBUG_DRIVER("... modeset : %d\n", nouveau_modeset); + DRM_DEBUG_DRIVER("... runpm : %d\n", nouveau_runtime_pm); + DRM_DEBUG_DRIVER("... vram_pushbuf : %d\n", nouveau_vram_pushbuf); + DRM_DEBUG_DRIVER("... pstate : %d\n", nouveau_pstate); +} + static const struct dev_pm_ops nouveau_pm_ops = { .suspend = nouveau_pmops_suspend, .resume = nouveau_pmops_resume, @@ -1037,7 +1057,7 @@ nouveau_platform_device_create_(struct platform_device *pdev, int size, if (err) return ERR_PTR(err); - drm = drm_dev_alloc(&driver, &pdev->dev); + drm = drm_dev_alloc(&driver_platform, &pdev->dev); if (!drm) { err = -ENOMEM; goto err_free; @@ -1062,6 +1082,13 @@ EXPORT_SYMBOL(nouveau_platform_device_create_); static int __init nouveau_drm_init(void) { + driver_pci = driver_stub; + driver_pci.set_busid = drm_pci_set_busid; + driver_platform = driver_stub; + driver_platform.set_busid = drm_platform_set_busid; + + nouveau_display_options(); + if (nouveau_modeset == -1) { #ifdef CONFIG_VGA_CONSOLE if (vgacon_text_force()) @@ -1073,7 +1100,7 @@ nouveau_drm_init(void) return 0; nouveau_register_dsm_handler(); - return drm_pci_init(&driver, &nouveau_drm_pci_driver); + return drm_pci_init(&driver_pci, &nouveau_drm_pci_driver); } static void __exit @@ -1082,7 +1109,7 @@ nouveau_drm_exit(void) if (!nouveau_modeset) return; - drm_pci_exit(&driver, &nouveau_drm_pci_driver); + drm_pci_exit(&driver_pci, &nouveau_drm_pci_driver); nouveau_unregister_dsm_handler(); } diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h index b02b02452c854c5fdab9ddd985dc12ce0f12e7af..8ae36f265fb8867b6092a48fcde8b06450148b40 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.h +++ b/drivers/gpu/drm/nouveau/nouveau_drm.h @@ -10,7 +10,7 @@ #define DRIVER_MAJOR 1 #define DRIVER_MINOR 2 -#define DRIVER_PATCHLEVEL 0 +#define DRIVER_PATCHLEVEL 1 /* * 1.1.1: @@ -26,6 +26,8 @@ * 1.2.0: * - object api exposed to userspace * - fermi,kepler,maxwell zbc + * 1.2.1: + * - allow concurrent access to bo's mapped read/write. */ #include diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 49fe6075cc7c50c79d47d2ac24c273e536ac3159..593ef8a2a069e16b4fdf96e9c95b67ca8ebd1b95 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -52,7 +52,7 @@ #include "nouveau_crtc.h" MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration"); -static int nouveau_nofbaccel = 0; +int nouveau_nofbaccel = 0; module_param_named(nofbaccel, nouveau_nofbaccel, int, 0400); static void @@ -308,7 +308,8 @@ static int nouveau_fbcon_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { - struct nouveau_fbdev *fbcon = (struct nouveau_fbdev *)helper; + struct nouveau_fbdev *fbcon = + container_of(helper, struct nouveau_fbdev, helper); struct drm_device *dev = fbcon->dev; struct nouveau_drm *drm = nouveau_drm(dev); struct nvif_device *device = &drm->device; diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h index 0b465c7d3907cb1a07667b667032dbe3b052c71b..6208e70e4a1cfdd16a949db884a605466d0ced38 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.h +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h @@ -73,5 +73,8 @@ void nouveau_fbcon_accel_save_disable(struct drm_device *dev); void nouveau_fbcon_accel_restore(struct drm_device *dev); void nouveau_fbcon_output_poll_changed(struct drm_device *dev); + +extern int nouveau_nofbaccel; + #endif /* __NV50_FBCON_H__ */ diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 0a93114158cde37b41a311d612e6205eb7a98ccb..515cd9aebb9982d1c0c0c06f1deaf4d0c7bf3e5c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -36,123 +37,234 @@ #include "nouveau_dma.h" #include "nouveau_fence.h" -struct fence_work { - struct work_struct base; - struct list_head head; - void (*func)(void *); - void *data; -}; +static const struct fence_ops nouveau_fence_ops_uevent; +static const struct fence_ops nouveau_fence_ops_legacy; + +static inline struct nouveau_fence * +from_fence(struct fence *fence) +{ + return container_of(fence, struct nouveau_fence, base); +} + +static inline struct nouveau_fence_chan * +nouveau_fctx(struct nouveau_fence *fence) +{ + return container_of(fence->base.lock, struct nouveau_fence_chan, lock); +} static void nouveau_fence_signal(struct nouveau_fence *fence) { - struct fence_work *work, *temp; + fence_signal_locked(&fence->base); + list_del(&fence->head); + + if (test_bit(FENCE_FLAG_USER_BITS, &fence->base.flags)) { + struct nouveau_fence_chan *fctx = nouveau_fctx(fence); - list_for_each_entry_safe(work, temp, &fence->work, head) { - schedule_work(&work->base); - list_del(&work->head); + if (!--fctx->notify_ref) + nvif_notify_put(&fctx->notify); } - fence->channel = NULL; - list_del(&fence->head); + fence_put(&fence->base); +} + +static struct nouveau_fence * +nouveau_local_fence(struct fence *fence, struct nouveau_drm *drm) { + struct nouveau_fence_priv *priv = (void*)drm->fence; + + if (fence->ops != &nouveau_fence_ops_legacy && + fence->ops != &nouveau_fence_ops_uevent) + return NULL; + + if (fence->context < priv->context_base || + fence->context >= priv->context_base + priv->contexts) + return NULL; + + return from_fence(fence); } void nouveau_fence_context_del(struct nouveau_fence_chan *fctx) { - struct nouveau_fence *fence, *fnext; - spin_lock(&fctx->lock); - list_for_each_entry_safe(fence, fnext, &fctx->pending, head) { + struct nouveau_fence *fence; + + nvif_notify_fini(&fctx->notify); + + spin_lock_irq(&fctx->lock); + while (!list_empty(&fctx->pending)) { + fence = list_entry(fctx->pending.next, typeof(*fence), head); + nouveau_fence_signal(fence); + fence->channel = NULL; } - spin_unlock(&fctx->lock); + spin_unlock_irq(&fctx->lock); +} + +static void +nouveau_fence_context_put(struct kref *fence_ref) +{ + kfree(container_of(fence_ref, struct nouveau_fence_chan, fence_ref)); } void -nouveau_fence_context_new(struct nouveau_fence_chan *fctx) +nouveau_fence_context_free(struct nouveau_fence_chan *fctx) +{ + kref_put(&fctx->fence_ref, nouveau_fence_context_put); +} + +static void +nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx) +{ + struct nouveau_fence *fence; + + u32 seq = fctx->read(chan); + + while (!list_empty(&fctx->pending)) { + fence = list_entry(fctx->pending.next, typeof(*fence), head); + + if ((int)(seq - fence->base.seqno) < 0) + return; + + nouveau_fence_signal(fence); + } +} + +static int +nouveau_fence_wait_uevent_handler(struct nvif_notify *notify) { + struct nouveau_fence_chan *fctx = + container_of(notify, typeof(*fctx), notify); + unsigned long flags; + + spin_lock_irqsave(&fctx->lock, flags); + if (!list_empty(&fctx->pending)) { + struct nouveau_fence *fence; + + fence = list_entry(fctx->pending.next, typeof(*fence), head); + nouveau_fence_update(fence->channel, fctx); + } + spin_unlock_irqrestore(&fctx->lock, flags); + + /* Always return keep here. NVIF refcount is handled with nouveau_fence_update */ + return NVIF_NOTIFY_KEEP; +} + +void +nouveau_fence_context_new(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx) +{ + struct nouveau_fence_priv *priv = (void*)chan->drm->fence; + struct nouveau_cli *cli = (void *)nvif_client(chan->object); + int ret; + INIT_LIST_HEAD(&fctx->flip); INIT_LIST_HEAD(&fctx->pending); spin_lock_init(&fctx->lock); + fctx->context = priv->context_base + chan->chid; + + if (chan == chan->drm->cechan) + strcpy(fctx->name, "copy engine channel"); + else if (chan == chan->drm->channel) + strcpy(fctx->name, "generic kernel channel"); + else + strcpy(fctx->name, nvkm_client(&cli->base)->name); + + kref_init(&fctx->fence_ref); + if (!priv->uevent) + return; + + ret = nvif_notify_init(chan->object, NULL, + nouveau_fence_wait_uevent_handler, false, + G82_CHANNEL_DMA_V0_NTFY_UEVENT, + &(struct nvif_notify_uevent_req) { }, + sizeof(struct nvif_notify_uevent_req), + sizeof(struct nvif_notify_uevent_rep), + &fctx->notify); + + WARN_ON(ret); } +struct nouveau_fence_work { + struct work_struct work; + struct fence_cb cb; + void (*func)(void *); + void *data; +}; + static void nouveau_fence_work_handler(struct work_struct *kwork) { - struct fence_work *work = container_of(kwork, typeof(*work), base); + struct nouveau_fence_work *work = container_of(kwork, typeof(*work), work); work->func(work->data); kfree(work); } +static void nouveau_fence_work_cb(struct fence *fence, struct fence_cb *cb) +{ + struct nouveau_fence_work *work = container_of(cb, typeof(*work), cb); + + schedule_work(&work->work); +} + void -nouveau_fence_work(struct nouveau_fence *fence, +nouveau_fence_work(struct fence *fence, void (*func)(void *), void *data) { - struct nouveau_channel *chan = fence->channel; - struct nouveau_fence_chan *fctx; - struct fence_work *work = NULL; + struct nouveau_fence_work *work; - if (nouveau_fence_done(fence)) { - func(data); - return; - } + if (fence_is_signaled(fence)) + goto err; - fctx = chan->fence; work = kmalloc(sizeof(*work), GFP_KERNEL); if (!work) { - WARN_ON(nouveau_fence_wait(fence, false, false)); - func(data); - return; - } - - spin_lock(&fctx->lock); - if (!fence->channel) { - spin_unlock(&fctx->lock); - kfree(work); - func(data); - return; + /* + * this might not be a nouveau fence any more, + * so force a lazy wait here + */ + WARN_ON(nouveau_fence_wait((struct nouveau_fence *)fence, + true, false)); + goto err; } - INIT_WORK(&work->base, nouveau_fence_work_handler); + INIT_WORK(&work->work, nouveau_fence_work_handler); work->func = func; work->data = data; - list_add(&work->head, &fence->work); - spin_unlock(&fctx->lock); -} - -static void -nouveau_fence_update(struct nouveau_channel *chan) -{ - struct nouveau_fence_chan *fctx = chan->fence; - struct nouveau_fence *fence, *fnext; - spin_lock(&fctx->lock); - list_for_each_entry_safe(fence, fnext, &fctx->pending, head) { - if (fctx->read(chan) < fence->sequence) - break; + if (fence_add_callback(fence, &work->cb, nouveau_fence_work_cb) < 0) + goto err_free; + return; - nouveau_fence_signal(fence); - nouveau_fence_unref(&fence); - } - spin_unlock(&fctx->lock); +err_free: + kfree(work); +err: + func(data); } int nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan) { struct nouveau_fence_chan *fctx = chan->fence; + struct nouveau_fence_priv *priv = (void*)chan->drm->fence; int ret; fence->channel = chan; fence->timeout = jiffies + (15 * HZ); - fence->sequence = ++fctx->sequence; + if (priv->uevent) + fence_init(&fence->base, &nouveau_fence_ops_uevent, + &fctx->lock, fctx->context, ++fctx->sequence); + else + fence_init(&fence->base, &nouveau_fence_ops_legacy, + &fctx->lock, fctx->context, ++fctx->sequence); + kref_get(&fctx->fence_ref); + + trace_fence_emit(&fence->base); ret = fctx->emit(fence); if (!ret) { - kref_get(&fence->kref); - spin_lock(&fctx->lock); + fence_get(&fence->base); + spin_lock_irq(&fctx->lock); + nouveau_fence_update(chan, fctx); list_add_tail(&fence->head, &fctx->pending); - spin_unlock(&fctx->lock); + spin_unlock_irq(&fctx->lock); } return ret; @@ -161,114 +273,70 @@ nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan) bool nouveau_fence_done(struct nouveau_fence *fence) { - if (fence->channel) - nouveau_fence_update(fence->channel); - return !fence->channel; -} + if (fence->base.ops == &nouveau_fence_ops_legacy || + fence->base.ops == &nouveau_fence_ops_uevent) { + struct nouveau_fence_chan *fctx = nouveau_fctx(fence); + unsigned long flags; -struct nouveau_fence_wait { - struct nouveau_fence_priv *priv; - struct nvif_notify notify; -}; + if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags)) + return true; -static int -nouveau_fence_wait_uevent_handler(struct nvif_notify *notify) -{ - struct nouveau_fence_wait *wait = - container_of(notify, typeof(*wait), notify); - wake_up_all(&wait->priv->waiting); - return NVIF_NOTIFY_KEEP; + spin_lock_irqsave(&fctx->lock, flags); + nouveau_fence_update(fence->channel, fctx); + spin_unlock_irqrestore(&fctx->lock, flags); + } + return fence_is_signaled(&fence->base); } -static int -nouveau_fence_wait_uevent(struct nouveau_fence *fence, bool intr) - +static long +nouveau_fence_wait_legacy(struct fence *f, bool intr, long wait) { - struct nouveau_channel *chan = fence->channel; - struct nouveau_fence_priv *priv = chan->drm->fence; - struct nouveau_fence_wait wait = { .priv = priv }; - int ret = 0; + struct nouveau_fence *fence = from_fence(f); + unsigned long sleep_time = NSEC_PER_MSEC / 1000; + unsigned long t = jiffies, timeout = t + wait; - ret = nvif_notify_init(chan->object, NULL, - nouveau_fence_wait_uevent_handler, false, - G82_CHANNEL_DMA_V0_NTFY_UEVENT, - &(struct nvif_notify_uevent_req) { - }, - sizeof(struct nvif_notify_uevent_req), - sizeof(struct nvif_notify_uevent_rep), - &wait.notify); - if (ret) - return ret; + while (!nouveau_fence_done(fence)) { + ktime_t kt; - nvif_notify_get(&wait.notify); - - if (fence->timeout) { - unsigned long timeout = fence->timeout - jiffies; - - if (time_before(jiffies, fence->timeout)) { - if (intr) { - ret = wait_event_interruptible_timeout( - priv->waiting, - nouveau_fence_done(fence), - timeout); - } else { - ret = wait_event_timeout(priv->waiting, - nouveau_fence_done(fence), - timeout); - } - } + t = jiffies; - if (ret >= 0) { - fence->timeout = jiffies + ret; - if (time_after_eq(jiffies, fence->timeout)) - ret = -EBUSY; - } - } else { - if (intr) { - ret = wait_event_interruptible(priv->waiting, - nouveau_fence_done(fence)); - } else { - wait_event(priv->waiting, nouveau_fence_done(fence)); + if (wait != MAX_SCHEDULE_TIMEOUT && time_after_eq(t, timeout)) { + __set_current_state(TASK_RUNNING); + return 0; } + + __set_current_state(intr ? TASK_INTERRUPTIBLE : + TASK_UNINTERRUPTIBLE); + + kt = ktime_set(0, sleep_time); + schedule_hrtimeout(&kt, HRTIMER_MODE_REL); + sleep_time *= 2; + if (sleep_time > NSEC_PER_MSEC) + sleep_time = NSEC_PER_MSEC; + + if (intr && signal_pending(current)) + return -ERESTARTSYS; } - nvif_notify_fini(&wait.notify); - if (unlikely(ret < 0)) - return ret; + __set_current_state(TASK_RUNNING); - return 0; + return timeout - t; } -int -nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr) +static int +nouveau_fence_wait_busy(struct nouveau_fence *fence, bool intr) { - struct nouveau_channel *chan = fence->channel; - struct nouveau_fence_priv *priv = chan ? chan->drm->fence : NULL; - unsigned long sleep_time = NSEC_PER_MSEC / 1000; - ktime_t t; int ret = 0; - while (priv && priv->uevent && lazy && !nouveau_fence_done(fence)) { - ret = nouveau_fence_wait_uevent(fence, intr); - if (ret < 0) - return ret; - } - while (!nouveau_fence_done(fence)) { - if (fence->timeout && time_after_eq(jiffies, fence->timeout)) { + if (time_after_eq(jiffies, fence->timeout)) { ret = -EBUSY; break; } - __set_current_state(intr ? TASK_INTERRUPTIBLE : - TASK_UNINTERRUPTIBLE); - if (lazy) { - t = ktime_set(0, sleep_time); - schedule_hrtimeout(&t, HRTIMER_MODE_REL); - sleep_time *= 2; - if (sleep_time > NSEC_PER_MSEC) - sleep_time = NSEC_PER_MSEC; - } + __set_current_state(intr ? + TASK_INTERRUPTIBLE : + TASK_UNINTERRUPTIBLE); if (intr && signal_pending(current)) { ret = -ERESTARTSYS; @@ -281,47 +349,86 @@ nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr) } int -nouveau_fence_sync(struct nouveau_fence *fence, struct nouveau_channel *chan) +nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr) { - struct nouveau_fence_chan *fctx = chan->fence; - struct nouveau_channel *prev; - int ret = 0; + long ret; - prev = fence ? fence->channel : NULL; - if (prev) { - if (unlikely(prev != chan && !nouveau_fence_done(fence))) { - ret = fctx->sync(fence, prev, chan); - if (unlikely(ret)) - ret = nouveau_fence_wait(fence, true, false); - } - } + if (!lazy) + return nouveau_fence_wait_busy(fence, intr); - return ret; + ret = fence_wait_timeout(&fence->base, intr, 15 * HZ); + if (ret < 0) + return ret; + else if (!ret) + return -EBUSY; + else + return 0; } -static void -nouveau_fence_del(struct kref *kref) +int +nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool exclusive, bool intr) { - struct nouveau_fence *fence = container_of(kref, typeof(*fence), kref); - kfree(fence); + struct nouveau_fence_chan *fctx = chan->fence; + struct fence *fence; + struct reservation_object *resv = nvbo->bo.resv; + struct reservation_object_list *fobj; + struct nouveau_fence *f; + int ret = 0, i; + + if (!exclusive) { + ret = reservation_object_reserve_shared(resv); + + if (ret) + return ret; + } + + fobj = reservation_object_get_list(resv); + fence = reservation_object_get_excl(resv); + + if (fence && (!exclusive || !fobj || !fobj->shared_count)) { + struct nouveau_channel *prev = NULL; + + f = nouveau_local_fence(fence, chan->drm); + if (f) + prev = f->channel; + + if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan)))) + ret = fence_wait(fence, intr); + + return ret; + } + + if (!exclusive || !fobj) + return ret; + + for (i = 0; i < fobj->shared_count && !ret; ++i) { + struct nouveau_channel *prev = NULL; + + fence = rcu_dereference_protected(fobj->shared[i], + reservation_object_held(resv)); + + f = nouveau_local_fence(fence, chan->drm); + if (f) + prev = f->channel; + + if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan)))) + ret = fence_wait(fence, intr); + + if (ret) + break; + } + + return ret; } void nouveau_fence_unref(struct nouveau_fence **pfence) { if (*pfence) - kref_put(&(*pfence)->kref, nouveau_fence_del); + fence_put(&(*pfence)->base); *pfence = NULL; } -struct nouveau_fence * -nouveau_fence_ref(struct nouveau_fence *fence) -{ - if (fence) - kref_get(&fence->kref); - return fence; -} - int nouveau_fence_new(struct nouveau_channel *chan, bool sysmem, struct nouveau_fence **pfence) @@ -336,9 +443,7 @@ nouveau_fence_new(struct nouveau_channel *chan, bool sysmem, if (!fence) return -ENOMEM; - INIT_LIST_HEAD(&fence->work); fence->sysmem = sysmem; - kref_init(&fence->kref); ret = nouveau_fence_emit(fence, chan); if (ret) @@ -347,3 +452,101 @@ nouveau_fence_new(struct nouveau_channel *chan, bool sysmem, *pfence = fence; return ret; } + +static const char *nouveau_fence_get_get_driver_name(struct fence *fence) +{ + return "nouveau"; +} + +static const char *nouveau_fence_get_timeline_name(struct fence *f) +{ + struct nouveau_fence *fence = from_fence(f); + struct nouveau_fence_chan *fctx = nouveau_fctx(fence); + + return fence->channel ? fctx->name : "dead channel"; +} + +/* + * In an ideal world, read would not assume the channel context is still alive. + * This function may be called from another device, running into free memory as a + * result. The drm node should still be there, so we can derive the index from + * the fence context. + */ +static bool nouveau_fence_is_signaled(struct fence *f) +{ + struct nouveau_fence *fence = from_fence(f); + struct nouveau_fence_chan *fctx = nouveau_fctx(fence); + struct nouveau_channel *chan = fence->channel; + + return (int)(fctx->read(chan) - fence->base.seqno) >= 0; +} + +static bool nouveau_fence_no_signaling(struct fence *f) +{ + struct nouveau_fence *fence = from_fence(f); + + /* + * caller should have a reference on the fence, + * else fence could get freed here + */ + WARN_ON(atomic_read(&fence->base.refcount.refcount) <= 1); + + /* + * This needs uevents to work correctly, but fence_add_callback relies on + * being able to enable signaling. It will still get signaled eventually, + * just not right away. + */ + if (nouveau_fence_is_signaled(f)) { + list_del(&fence->head); + + fence_put(&fence->base); + return false; + } + + return true; +} + +static void nouveau_fence_release(struct fence *f) +{ + struct nouveau_fence *fence = from_fence(f); + struct nouveau_fence_chan *fctx = nouveau_fctx(fence); + + kref_put(&fctx->fence_ref, nouveau_fence_context_put); + fence_free(&fence->base); +} + +static const struct fence_ops nouveau_fence_ops_legacy = { + .get_driver_name = nouveau_fence_get_get_driver_name, + .get_timeline_name = nouveau_fence_get_timeline_name, + .enable_signaling = nouveau_fence_no_signaling, + .signaled = nouveau_fence_is_signaled, + .wait = nouveau_fence_wait_legacy, + .release = nouveau_fence_release +}; + +static bool nouveau_fence_enable_signaling(struct fence *f) +{ + struct nouveau_fence *fence = from_fence(f); + struct nouveau_fence_chan *fctx = nouveau_fctx(fence); + bool ret; + + if (!fctx->notify_ref++) + nvif_notify_get(&fctx->notify); + + ret = nouveau_fence_no_signaling(f); + if (ret) + set_bit(FENCE_FLAG_USER_BITS, &fence->base.flags); + else if (!--fctx->notify_ref) + nvif_notify_put(&fctx->notify); + + return ret; +} + +static const struct fence_ops nouveau_fence_ops_uevent = { + .get_driver_name = nouveau_fence_get_get_driver_name, + .get_timeline_name = nouveau_fence_get_timeline_name, + .enable_signaling = nouveau_fence_enable_signaling, + .signaled = nouveau_fence_is_signaled, + .wait = fence_default_wait, + .release = NULL +}; diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h b/drivers/gpu/drm/nouveau/nouveau_fence.h index c57bb61da58c162d241be806028dcf78809ff4eb..943b0b17b1fc760296eccea9b409f03696a644fc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.h +++ b/drivers/gpu/drm/nouveau/nouveau_fence.h @@ -1,33 +1,37 @@ #ifndef __NOUVEAU_FENCE_H__ #define __NOUVEAU_FENCE_H__ +#include +#include + struct nouveau_drm; +struct nouveau_bo; struct nouveau_fence { + struct fence base; + struct list_head head; - struct list_head work; - struct kref kref; bool sysmem; struct nouveau_channel *channel; unsigned long timeout; - u32 sequence; }; int nouveau_fence_new(struct nouveau_channel *, bool sysmem, struct nouveau_fence **); -struct nouveau_fence * -nouveau_fence_ref(struct nouveau_fence *); void nouveau_fence_unref(struct nouveau_fence **); int nouveau_fence_emit(struct nouveau_fence *, struct nouveau_channel *); bool nouveau_fence_done(struct nouveau_fence *); -void nouveau_fence_work(struct nouveau_fence *, void (*)(void *), void *); +void nouveau_fence_work(struct fence *, void (*)(void *), void *); int nouveau_fence_wait(struct nouveau_fence *, bool lazy, bool intr); -int nouveau_fence_sync(struct nouveau_fence *, struct nouveau_channel *); +int nouveau_fence_sync(struct nouveau_bo *, struct nouveau_channel *, bool exclusive, bool intr); struct nouveau_fence_chan { + spinlock_t lock; + struct kref fence_ref; + struct list_head pending; struct list_head flip; @@ -38,8 +42,12 @@ struct nouveau_fence_chan { int (*emit32)(struct nouveau_channel *, u64, u32); int (*sync32)(struct nouveau_channel *, u64, u32); - spinlock_t lock; u32 sequence; + u32 context; + char name[32]; + + struct nvif_notify notify; + int notify_ref; }; struct nouveau_fence_priv { @@ -49,14 +57,15 @@ struct nouveau_fence_priv { int (*context_new)(struct nouveau_channel *); void (*context_del)(struct nouveau_channel *); - wait_queue_head_t waiting; + u32 contexts, context_base; bool uevent; }; #define nouveau_fence(drm) ((struct nouveau_fence_priv *)(drm)->fence) -void nouveau_fence_context_new(struct nouveau_fence_chan *); +void nouveau_fence_context_new(struct nouveau_channel *, struct nouveau_fence_chan *); void nouveau_fence_context_del(struct nouveau_fence_chan *); +void nouveau_fence_context_free(struct nouveau_fence_chan *); int nv04_fence_create(struct nouveau_drm *); int nv04_fence_mthd(struct nouveau_channel *, u32, u32, u32); diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 292a677bfed4ebc649d5ae283a96343773befcfb..36951ee4b1571807d9a1f85c796f00b232e043d6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -98,17 +98,23 @@ static void nouveau_gem_object_unmap(struct nouveau_bo *nvbo, struct nouveau_vma *vma) { const bool mapped = nvbo->bo.mem.mem_type != TTM_PL_SYSTEM; - struct nouveau_fence *fence = NULL; + struct reservation_object *resv = nvbo->bo.resv; + struct reservation_object_list *fobj; + struct fence *fence = NULL; + + fobj = reservation_object_get_list(resv); list_del(&vma->head); - if (mapped) { - spin_lock(&nvbo->bo.bdev->fence_lock); - fence = nouveau_fence_ref(nvbo->bo.sync_obj); - spin_unlock(&nvbo->bo.bdev->fence_lock); - } + if (fobj && fobj->shared_count > 1) + ttm_bo_wait(&nvbo->bo, true, false, false); + else if (fobj && fobj->shared_count == 1) + fence = rcu_dereference_protected(fobj->shared[0], + reservation_object_held(resv)); + else + fence = reservation_object_get_excl(nvbo->bo.resv); - if (fence) { + if (fence && mapped) { nouveau_fence_work(fence, nouveau_gem_object_delete, vma); } else { if (mapped) @@ -116,7 +122,6 @@ nouveau_gem_object_unmap(struct nouveau_bo *nvbo, struct nouveau_vma *vma) nouveau_vm_put(vma); kfree(vma); } - nouveau_fence_unref(&fence); } void @@ -160,7 +165,7 @@ nouveau_gem_new(struct drm_device *dev, int size, int align, uint32_t domain, flags |= TTM_PL_FLAG_SYSTEM; ret = nouveau_bo_new(dev, size, align, flags, tile_mode, - tile_flags, NULL, pnvbo); + tile_flags, NULL, NULL, pnvbo); if (ret) return ret; nvbo = *pnvbo; @@ -288,24 +293,23 @@ nouveau_gem_set_domain(struct drm_gem_object *gem, uint32_t read_domains, } struct validate_op { - struct list_head vram_list; - struct list_head gart_list; - struct list_head both_list; + struct list_head list; struct ww_acquire_ctx ticket; }; static void -validate_fini_list(struct list_head *list, struct nouveau_fence *fence, - struct ww_acquire_ctx *ticket) +validate_fini_no_ticket(struct validate_op *op, struct nouveau_fence *fence, + struct drm_nouveau_gem_pushbuf_bo *pbbo) { - struct list_head *entry, *tmp; struct nouveau_bo *nvbo; + struct drm_nouveau_gem_pushbuf_bo *b; - list_for_each_safe(entry, tmp, list) { - nvbo = list_entry(entry, struct nouveau_bo, entry); + while (!list_empty(&op->list)) { + nvbo = list_entry(op->list.next, struct nouveau_bo, entry); + b = &pbbo[nvbo->pbbo_index]; if (likely(fence)) - nouveau_bo_fence(nvbo, fence); + nouveau_bo_fence(nvbo, fence, !!b->write_domains); if (unlikely(nvbo->validate_mapped)) { ttm_bo_kunmap(&nvbo->kmap); @@ -314,23 +318,16 @@ validate_fini_list(struct list_head *list, struct nouveau_fence *fence, list_del(&nvbo->entry); nvbo->reserved_by = NULL; - ttm_bo_unreserve_ticket(&nvbo->bo, ticket); + ttm_bo_unreserve_ticket(&nvbo->bo, &op->ticket); drm_gem_object_unreference_unlocked(&nvbo->gem); } } static void -validate_fini_no_ticket(struct validate_op *op, struct nouveau_fence *fence) +validate_fini(struct validate_op *op, struct nouveau_fence *fence, + struct drm_nouveau_gem_pushbuf_bo *pbbo) { - validate_fini_list(&op->vram_list, fence, &op->ticket); - validate_fini_list(&op->gart_list, fence, &op->ticket); - validate_fini_list(&op->both_list, fence, &op->ticket); -} - -static void -validate_fini(struct validate_op *op, struct nouveau_fence *fence) -{ - validate_fini_no_ticket(op, fence); + validate_fini_no_ticket(op, fence, pbbo); ww_acquire_fini(&op->ticket); } @@ -344,6 +341,9 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv, int trycnt = 0; int ret, i; struct nouveau_bo *res_bo = NULL; + LIST_HEAD(gart_list); + LIST_HEAD(vram_list); + LIST_HEAD(both_list); ww_acquire_init(&op->ticket, &reservation_ww_class); retry: @@ -360,9 +360,8 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv, gem = drm_gem_object_lookup(dev, file_priv, b->handle); if (!gem) { NV_PRINTK(error, cli, "Unknown handle 0x%08x\n", b->handle); - ww_acquire_done(&op->ticket); - validate_fini(op, NULL); - return -ENOENT; + ret = -ENOENT; + break; } nvbo = nouveau_gem_object(gem); if (nvbo == res_bo) { @@ -375,14 +374,16 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv, NV_PRINTK(error, cli, "multiple instances of buffer %d on " "validation list\n", b->handle); drm_gem_object_unreference_unlocked(gem); - ww_acquire_done(&op->ticket); - validate_fini(op, NULL); - return -EINVAL; + ret = -EINVAL; + break; } ret = ttm_bo_reserve(&nvbo->bo, true, false, true, &op->ticket); if (ret) { - validate_fini_no_ticket(op, NULL); + list_splice_tail_init(&vram_list, &op->list); + list_splice_tail_init(&gart_list, &op->list); + list_splice_tail_init(&both_list, &op->list); + validate_fini_no_ticket(op, NULL, NULL); if (unlikely(ret == -EDEADLK)) { ret = ttm_bo_reserve_slowpath(&nvbo->bo, true, &op->ticket); @@ -390,12 +391,9 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv, res_bo = nvbo; } if (unlikely(ret)) { - ww_acquire_done(&op->ticket); - ww_acquire_fini(&op->ticket); - drm_gem_object_unreference_unlocked(gem); if (ret != -ERESTARTSYS) NV_PRINTK(error, cli, "fail reserve\n"); - return ret; + break; } } @@ -404,45 +402,32 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv, nvbo->pbbo_index = i; if ((b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) && (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART)) - list_add_tail(&nvbo->entry, &op->both_list); + list_add_tail(&nvbo->entry, &both_list); else if (b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) - list_add_tail(&nvbo->entry, &op->vram_list); + list_add_tail(&nvbo->entry, &vram_list); else if (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART) - list_add_tail(&nvbo->entry, &op->gart_list); + list_add_tail(&nvbo->entry, &gart_list); else { NV_PRINTK(error, cli, "invalid valid domains: 0x%08x\n", b->valid_domains); - list_add_tail(&nvbo->entry, &op->both_list); - ww_acquire_done(&op->ticket); - validate_fini(op, NULL); - return -EINVAL; + list_add_tail(&nvbo->entry, &both_list); + ret = -EINVAL; + break; } if (nvbo == res_bo) goto retry; } ww_acquire_done(&op->ticket); - return 0; -} - -static int -validate_sync(struct nouveau_channel *chan, struct nouveau_bo *nvbo) -{ - struct nouveau_fence *fence = NULL; - int ret = 0; - - spin_lock(&nvbo->bo.bdev->fence_lock); - fence = nouveau_fence_ref(nvbo->bo.sync_obj); - spin_unlock(&nvbo->bo.bdev->fence_lock); - - if (fence) { - ret = nouveau_fence_sync(fence, chan); - nouveau_fence_unref(&fence); - } - + list_splice_tail(&vram_list, &op->list); + list_splice_tail(&gart_list, &op->list); + list_splice_tail(&both_list, &op->list); + if (ret) + validate_fini(op, NULL, NULL); return ret; + } static int @@ -474,9 +459,10 @@ validate_list(struct nouveau_channel *chan, struct nouveau_cli *cli, return ret; } - ret = validate_sync(chan, nvbo); + ret = nouveau_fence_sync(nvbo, chan, !!b->write_domains, true); if (unlikely(ret)) { - NV_PRINTK(error, cli, "fail post-validate sync\n"); + if (ret != -ERESTARTSYS) + NV_PRINTK(error, cli, "fail post-validate sync\n"); return ret; } @@ -513,11 +499,9 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan, struct validate_op *op, int *apply_relocs) { struct nouveau_cli *cli = nouveau_cli(file_priv); - int ret, relocs = 0; + int ret; - INIT_LIST_HEAD(&op->vram_list); - INIT_LIST_HEAD(&op->gart_list); - INIT_LIST_HEAD(&op->both_list); + INIT_LIST_HEAD(&op->list); if (nr_buffers == 0) return 0; @@ -529,34 +513,14 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan, return ret; } - ret = validate_list(chan, cli, &op->vram_list, pbbo, user_buffers); - if (unlikely(ret < 0)) { - if (ret != -ERESTARTSYS) - NV_PRINTK(error, cli, "validate vram_list\n"); - validate_fini(op, NULL); - return ret; - } - relocs += ret; - - ret = validate_list(chan, cli, &op->gart_list, pbbo, user_buffers); - if (unlikely(ret < 0)) { - if (ret != -ERESTARTSYS) - NV_PRINTK(error, cli, "validate gart_list\n"); - validate_fini(op, NULL); - return ret; - } - relocs += ret; - - ret = validate_list(chan, cli, &op->both_list, pbbo, user_buffers); + ret = validate_list(chan, cli, &op->list, pbbo, user_buffers); if (unlikely(ret < 0)) { if (ret != -ERESTARTSYS) - NV_PRINTK(error, cli, "validate both_list\n"); - validate_fini(op, NULL); + NV_PRINTK(error, cli, "validating bo list\n"); + validate_fini(op, NULL, NULL); return ret; } - relocs += ret; - - *apply_relocs = relocs; + *apply_relocs = ret; return 0; } @@ -659,9 +623,7 @@ nouveau_gem_pushbuf_reloc_apply(struct nouveau_cli *cli, data |= r->vor; } - spin_lock(&nvbo->bo.bdev->fence_lock); - ret = ttm_bo_wait(&nvbo->bo, false, false, false); - spin_unlock(&nvbo->bo.bdev->fence_lock); + ret = ttm_bo_wait(&nvbo->bo, true, false, false); if (ret) { NV_PRINTK(error, cli, "reloc wait_idle failed: %d\n", ret); break; @@ -839,7 +801,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, } out: - validate_fini(&op, fence); + validate_fini(&op, fence, bo); nouveau_fence_unref(&fence); out_prevalid: @@ -884,17 +846,29 @@ nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data, struct drm_gem_object *gem; struct nouveau_bo *nvbo; bool no_wait = !!(req->flags & NOUVEAU_GEM_CPU_PREP_NOWAIT); - int ret = -EINVAL; + bool write = !!(req->flags & NOUVEAU_GEM_CPU_PREP_WRITE); + int ret; gem = drm_gem_object_lookup(dev, file_priv, req->handle); if (!gem) return -ENOENT; nvbo = nouveau_gem_object(gem); - spin_lock(&nvbo->bo.bdev->fence_lock); - ret = ttm_bo_wait(&nvbo->bo, true, true, no_wait); - spin_unlock(&nvbo->bo.bdev->fence_lock); + if (no_wait) + ret = reservation_object_test_signaled_rcu(nvbo->bo.resv, write) ? 0 : -EBUSY; + else { + long lret; + + lret = reservation_object_wait_timeout_rcu(nvbo->bo.resv, write, true, 30 * HZ); + if (!lret) + ret = -EBUSY; + else if (lret > 0) + ret = 0; + else + ret = lret; + } drm_gem_object_unreference_unlocked(gem); + return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.h b/drivers/gpu/drm/nouveau/nouveau_gem.h index ddab762d81fef78d269729aa8c906934ef9e1d3b..e4049faca780453f07dccbf034950a8215fa786e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.h +++ b/drivers/gpu/drm/nouveau/nouveau_gem.h @@ -39,7 +39,7 @@ struct reservation_object *nouveau_gem_prime_res_obj(struct drm_gem_object *); extern void nouveau_gem_prime_unpin(struct drm_gem_object *); extern struct sg_table *nouveau_gem_prime_get_sg_table(struct drm_gem_object *); extern struct drm_gem_object *nouveau_gem_prime_import_sg_table( - struct drm_device *, size_t size, struct sg_table *); + struct drm_device *, struct dma_buf_attachment *, struct sg_table *); extern void *nouveau_gem_prime_vmap(struct drm_gem_object *); extern void nouveau_gem_prime_vunmap(struct drm_gem_object *, void *); diff --git a/drivers/gpu/drm/nouveau/nouveau_prime.c b/drivers/gpu/drm/nouveau/nouveau_prime.c index 1f51008e4d26e4511a6260d606ca515ed025d160..228226ab27fc1b55883d9d7bcf6066f5c3852bc5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_prime.c +++ b/drivers/gpu/drm/nouveau/nouveau_prime.c @@ -23,6 +23,7 @@ */ #include +#include #include "nouveau_drm.h" #include "nouveau_gem.h" @@ -56,17 +57,20 @@ void nouveau_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) } struct drm_gem_object *nouveau_gem_prime_import_sg_table(struct drm_device *dev, - size_t size, + struct dma_buf_attachment *attach, struct sg_table *sg) { struct nouveau_bo *nvbo; + struct reservation_object *robj = attach->dmabuf->resv; u32 flags = 0; int ret; flags = TTM_PL_FLAG_TT; - ret = nouveau_bo_new(dev, size, 0, flags, 0, 0, - sg, &nvbo); + ww_mutex_lock(&robj->lock, NULL); + ret = nouveau_bo_new(dev, attach->dmabuf->size, 0, flags, 0, 0, + sg, robj, &nvbo); + ww_mutex_unlock(&robj->lock); if (ret) return ERR_PTR(ret); diff --git a/drivers/gpu/drm/nouveau/nouveau_sysfs.c b/drivers/gpu/drm/nouveau/nouveau_sysfs.c index 3c6962d15b26c2099bc4f2e1d840f5377b809227..8fbbf3093d8611e9497eaacdad73ec698bf42b10 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sysfs.c +++ b/drivers/gpu/drm/nouveau/nouveau_sysfs.c @@ -29,7 +29,7 @@ #include "nouveau_sysfs.h" MODULE_PARM_DESC(pstate, "enable sysfs pstate file, which will be moved in the future"); -static int nouveau_pstate; +int nouveau_pstate; module_param_named(pstate, nouveau_pstate, int, 0400); static inline struct drm_device * diff --git a/drivers/gpu/drm/nouveau/nouveau_sysfs.h b/drivers/gpu/drm/nouveau/nouveau_sysfs.h index f973378160f8ca3410f06ad1fc1f4f7da6bd1816..4e5ea9241b2871bb2edcdf6a0dd9ef3d42e70933 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sysfs.h +++ b/drivers/gpu/drm/nouveau/nouveau_sysfs.h @@ -16,4 +16,6 @@ nouveau_sysfs(struct drm_device *dev) int nouveau_sysfs_init(struct drm_device *); void nouveau_sysfs_fini(struct drm_device *); +extern int nouveau_pstate; + #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c index 53874b76b031081b38853a400eaa2f1dc2f199e2..753a6def61e7ce474d73b21b9affe84ae8b1a861 100644 --- a/drivers/gpu/drm/nouveau/nouveau_ttm.c +++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c @@ -71,8 +71,7 @@ nouveau_vram_manager_del(struct ttm_mem_type_manager *man, static int nouveau_vram_manager_new(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, - struct ttm_placement *placement, - uint32_t flags, + const struct ttm_place *place, struct ttm_mem_reg *mem) { struct nouveau_drm *drm = nouveau_bdev(man->bdev); @@ -158,8 +157,7 @@ nouveau_gart_manager_del(struct ttm_mem_type_manager *man, static int nouveau_gart_manager_new(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, - struct ttm_placement *placement, - uint32_t flags, + const struct ttm_place *place, struct ttm_mem_reg *mem) { struct nouveau_drm *drm = nouveau_bdev(bo->bdev); @@ -239,8 +237,7 @@ nv04_gart_manager_del(struct ttm_mem_type_manager *man, struct ttm_mem_reg *mem) static int nv04_gart_manager_new(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, - struct ttm_placement *placement, - uint32_t flags, + const struct ttm_place *place, struct ttm_mem_reg *mem) { struct nouveau_mem *node; @@ -284,7 +281,7 @@ nouveau_ttm_mmap(struct file *filp, struct vm_area_struct *vma) struct nouveau_drm *drm = nouveau_drm(file_priv->minor->dev); if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) - return drm_mmap(filp, vma); + return -EINVAL; return ttm_bo_mmap(filp, vma, &drm->ttm.bdev); } diff --git a/drivers/gpu/drm/nouveau/nv04_fence.c b/drivers/gpu/drm/nouveau/nv04_fence.c index 239c2c5a96159498f2ea2fee1ff235db6c9ecf56..f9859deb108a460d34e3740746c691e628cbea4d 100644 --- a/drivers/gpu/drm/nouveau/nv04_fence.c +++ b/drivers/gpu/drm/nouveau/nv04_fence.c @@ -41,7 +41,7 @@ nv04_fence_emit(struct nouveau_fence *fence) int ret = RING_SPACE(chan, 2); if (ret == 0) { BEGIN_NV04(chan, NvSubSw, 0x0150, 1); - OUT_RING (chan, fence->sequence); + OUT_RING (chan, fence->base.seqno); FIRE_RING (chan); } return ret; @@ -67,7 +67,7 @@ nv04_fence_context_del(struct nouveau_channel *chan) struct nv04_fence_chan *fctx = chan->fence; nouveau_fence_context_del(&fctx->base); chan->fence = NULL; - kfree(fctx); + nouveau_fence_context_free(&fctx->base); } static int @@ -75,7 +75,7 @@ nv04_fence_context_new(struct nouveau_channel *chan) { struct nv04_fence_chan *fctx = kzalloc(sizeof(*fctx), GFP_KERNEL); if (fctx) { - nouveau_fence_context_new(&fctx->base); + nouveau_fence_context_new(chan, &fctx->base); fctx->base.emit = nv04_fence_emit; fctx->base.sync = nv04_fence_sync; fctx->base.read = nv04_fence_read; @@ -105,5 +105,7 @@ nv04_fence_create(struct nouveau_drm *drm) priv->base.dtor = nv04_fence_destroy; priv->base.context_new = nv04_fence_context_new; priv->base.context_del = nv04_fence_context_del; + priv->base.contexts = 15; + priv->base.context_base = fence_context_alloc(priv->base.contexts); return 0; } diff --git a/drivers/gpu/drm/nouveau/nv10_fence.c b/drivers/gpu/drm/nouveau/nv10_fence.c index 4faaf0acf5d7d919769650662e1788aa861904e2..5e1ea1cdce75e907ccc75b25830be9b969daa339 100644 --- a/drivers/gpu/drm/nouveau/nv10_fence.c +++ b/drivers/gpu/drm/nouveau/nv10_fence.c @@ -33,7 +33,7 @@ nv10_fence_emit(struct nouveau_fence *fence) int ret = RING_SPACE(chan, 2); if (ret == 0) { BEGIN_NV04(chan, 0, NV10_SUBCHAN_REF_CNT, 1); - OUT_RING (chan, fence->sequence); + OUT_RING (chan, fence->base.seqno); FIRE_RING (chan); } return ret; @@ -63,7 +63,7 @@ nv10_fence_context_del(struct nouveau_channel *chan) nvif_object_fini(&fctx->head[i]); nvif_object_fini(&fctx->sema); chan->fence = NULL; - kfree(fctx); + nouveau_fence_context_free(&fctx->base); } int @@ -75,7 +75,7 @@ nv10_fence_context_new(struct nouveau_channel *chan) if (!fctx) return -ENOMEM; - nouveau_fence_context_new(&fctx->base); + nouveau_fence_context_new(chan, &fctx->base); fctx->base.emit = nv10_fence_emit; fctx->base.read = nv10_fence_read; fctx->base.sync = nv10_fence_sync; @@ -106,6 +106,8 @@ nv10_fence_create(struct nouveau_drm *drm) priv->base.dtor = nv10_fence_destroy; priv->base.context_new = nv10_fence_context_new; priv->base.context_del = nv10_fence_context_del; + priv->base.contexts = 31; + priv->base.context_base = fence_context_alloc(priv->base.contexts); spin_lock_init(&priv->lock); return 0; } diff --git a/drivers/gpu/drm/nouveau/nv17_fence.c b/drivers/gpu/drm/nouveau/nv17_fence.c index ca907479f92fc6c42ee0647c11e1881e079ea74d..40b461c7d5c565c0471c5f3b80f09221a7d42a8b 100644 --- a/drivers/gpu/drm/nouveau/nv17_fence.c +++ b/drivers/gpu/drm/nouveau/nv17_fence.c @@ -84,7 +84,7 @@ nv17_fence_context_new(struct nouveau_channel *chan) if (!fctx) return -ENOMEM; - nouveau_fence_context_new(&fctx->base); + nouveau_fence_context_new(chan, &fctx->base); fctx->base.emit = nv10_fence_emit; fctx->base.read = nv10_fence_read; fctx->base.sync = nv17_fence_sync; @@ -124,10 +124,12 @@ nv17_fence_create(struct nouveau_drm *drm) priv->base.resume = nv17_fence_resume; priv->base.context_new = nv17_fence_context_new; priv->base.context_del = nv10_fence_context_del; + priv->base.contexts = 31; + priv->base.context_base = fence_context_alloc(priv->base.contexts); spin_lock_init(&priv->lock); ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, &priv->bo); + 0, 0x0000, NULL, NULL, &priv->bo); if (!ret) { ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM); if (!ret) { diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 03949eaa629f772e976070f09da30c58063022d0..ae873d1a8d463f7cd55da92b550eec1c44d31e25 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -1066,7 +1066,7 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1; u32 hactive, hsynce, hbackp, hfrontp, hblanke, hblanks; u32 vactive, vsynce, vbackp, vfrontp, vblanke, vblanks; - u32 vblan2e = 0, vblan2s = 1; + u32 vblan2e = 0, vblan2s = 1, vblankus = 0; u32 *push; int ret; @@ -1083,6 +1083,11 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, vblanke = vsynce + vbackp; vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace; vblanks = vactive - vfrontp - 1; + /* XXX: Safe underestimate, even "0" works */ + vblankus = (vactive - mode->vdisplay - 2) * hactive; + vblankus *= 1000; + vblankus /= mode->clock; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { vblan2e = vactive + vsynce + vbackp; vblan2s = vblan2e + (mode->vdisplay * vscan / ilace); @@ -1099,14 +1104,14 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, evo_mthd(push, 0x0804 + (nv_crtc->index * 0x400), 2); evo_data(push, 0x00800000 | mode->clock); evo_data(push, (ilace == 2) ? 2 : 0); - evo_mthd(push, 0x0810 + (nv_crtc->index * 0x400), 6); + evo_mthd(push, 0x0810 + (nv_crtc->index * 0x400), 8); evo_data(push, 0x00000000); evo_data(push, (vactive << 16) | hactive); evo_data(push, ( vsynce << 16) | hsynce); evo_data(push, (vblanke << 16) | hblanke); evo_data(push, (vblanks << 16) | hblanks); evo_data(push, (vblan2e << 16) | vblan2s); - evo_mthd(push, 0x082c + (nv_crtc->index * 0x400), 1); + evo_data(push, vblankus); evo_data(push, 0x00000000); evo_mthd(push, 0x0900 + (nv_crtc->index * 0x400), 2); evo_data(push, 0x00000311); @@ -1378,7 +1383,7 @@ nv50_crtc_create(struct drm_device *dev, int index) drm_mode_crtc_set_gamma_size(crtc, 256); ret = nouveau_bo_new(dev, 8192, 0x100, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, &head->base.lut.nvbo); + 0, 0x0000, NULL, NULL, &head->base.lut.nvbo); if (!ret) { ret = nouveau_bo_pin(head->base.lut.nvbo, TTM_PL_FLAG_VRAM); if (!ret) { @@ -1401,7 +1406,7 @@ nv50_crtc_create(struct drm_device *dev, int index) goto out; ret = nouveau_bo_new(dev, 64 * 64 * 4, 0x100, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, &head->base.cursor.nvbo); + 0, 0x0000, NULL, NULL, &head->base.cursor.nvbo); if (!ret) { ret = nouveau_bo_pin(head->base.cursor.nvbo, TTM_PL_FLAG_VRAM); if (!ret) { @@ -1651,17 +1656,21 @@ static void nv50_audio_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); struct nouveau_connector *nv_connector; struct nv50_disp *disp = nv50_disp(encoder->dev); - struct { - struct nv50_disp_mthd_v1 base; - struct nv50_disp_sor_hda_eld_v0 eld; + struct __packed { + struct { + struct nv50_disp_mthd_v1 mthd; + struct nv50_disp_sor_hda_eld_v0 eld; + } base; u8 data[sizeof(nv_connector->base.eld)]; } args = { - .base.version = 1, - .base.method = NV50_DISP_MTHD_V1_SOR_HDA_ELD, - .base.hasht = nv_encoder->dcb->hasht, - .base.hashm = nv_encoder->dcb->hashm, + .base.mthd.version = 1, + .base.mthd.method = NV50_DISP_MTHD_V1_SOR_HDA_ELD, + .base.mthd.hasht = nv_encoder->dcb->hasht, + .base.mthd.hashm = (0xf0ff & nv_encoder->dcb->hashm) | + (0x0100 << nv_crtc->index), }; nv_connector = nouveau_encoder_connector_get(nv_encoder); @@ -1671,11 +1680,11 @@ nv50_audio_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode) drm_edid_to_eld(&nv_connector->base, nv_connector->edid); memcpy(args.data, nv_connector->base.eld, sizeof(args.data)); - nvif_mthd(disp->disp, 0, &args, sizeof(args)); + nvif_mthd(disp->disp, 0, &args, sizeof(args.base) + args.data[2] * 4); } static void -nv50_audio_disconnect(struct drm_encoder *encoder) +nv50_audio_disconnect(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nv50_disp *disp = nv50_disp(encoder->dev); @@ -1686,7 +1695,8 @@ nv50_audio_disconnect(struct drm_encoder *encoder) .base.version = 1, .base.method = NV50_DISP_MTHD_V1_SOR_HDA_ELD, .base.hasht = nv_encoder->dcb->hasht, - .base.hashm = nv_encoder->dcb->hashm, + .base.hashm = (0xf0ff & nv_encoder->dcb->hashm) | + (0x0100 << nv_crtc->index), }; nvif_mthd(disp->disp, 0, &args, sizeof(args)); @@ -1745,8 +1755,6 @@ nv50_hdmi_disconnect(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) (0x0100 << nv_crtc->index), }; - nv50_audio_disconnect(encoder); - nvif_mthd(disp->disp, 0, &args, sizeof(args)); } @@ -1855,6 +1863,7 @@ nv50_sor_disconnect(struct drm_encoder *encoder) if (nv_crtc) { nv50_crtc_prepare(&nv_crtc->base); nv50_sor_ctrl(nv_encoder, 1 << nv_crtc->index, 0); + nv50_audio_disconnect(encoder, nv_crtc); nv50_hdmi_disconnect(&nv_encoder->base.base, nv_crtc); } } @@ -1954,6 +1963,7 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, proto = 0x8; else proto = 0x9; + nv50_audio_mode_set(encoder, mode); break; default: BUG_ON(1); @@ -2458,7 +2468,7 @@ nv50_display_create(struct drm_device *dev) /* small shared memory area we use for notifiers and semaphores */ ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, &disp->sync); + 0, 0x0000, NULL, NULL, &disp->sync); if (!ret) { ret = nouveau_bo_pin(disp->sync, TTM_PL_FLAG_VRAM); if (!ret) { diff --git a/drivers/gpu/drm/nouveau/nv50_fence.c b/drivers/gpu/drm/nouveau/nv50_fence.c index 195cf51a7c31e187717f11b5542656247905a28b..22d242b37962cc14990174bc97f6876a30e31781 100644 --- a/drivers/gpu/drm/nouveau/nv50_fence.c +++ b/drivers/gpu/drm/nouveau/nv50_fence.c @@ -46,7 +46,7 @@ nv50_fence_context_new(struct nouveau_channel *chan) if (!fctx) return -ENOMEM; - nouveau_fence_context_new(&fctx->base); + nouveau_fence_context_new(chan, &fctx->base); fctx->base.emit = nv10_fence_emit; fctx->base.read = nv10_fence_read; fctx->base.sync = nv17_fence_sync; @@ -95,10 +95,12 @@ nv50_fence_create(struct nouveau_drm *drm) priv->base.resume = nv17_fence_resume; priv->base.context_new = nv50_fence_context_new; priv->base.context_del = nv10_fence_context_del; + priv->base.contexts = 127; + priv->base.context_base = fence_context_alloc(priv->base.contexts); spin_lock_init(&priv->lock); ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, &priv->bo); + 0, 0x0000, NULL, NULL, &priv->bo); if (!ret) { ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM); if (!ret) { diff --git a/drivers/gpu/drm/nouveau/nv84_fence.c b/drivers/gpu/drm/nouveau/nv84_fence.c index 933a779c93ab7c132b82eed2f0e2bb5692462cc0..d6c6c87c3f07e2e929e49cf8ca7d41145f8c164b 100644 --- a/drivers/gpu/drm/nouveau/nv84_fence.c +++ b/drivers/gpu/drm/nouveau/nv84_fence.c @@ -82,7 +82,7 @@ nv84_fence_emit(struct nouveau_fence *fence) else addr += fctx->vma.offset; - return fctx->base.emit32(chan, addr, fence->sequence); + return fctx->base.emit32(chan, addr, fence->base.seqno); } static int @@ -97,7 +97,7 @@ nv84_fence_sync(struct nouveau_fence *fence, else addr += fctx->vma.offset; - return fctx->base.sync32(chan, addr, fence->sequence); + return fctx->base.sync32(chan, addr, fence->base.seqno); } static u32 @@ -120,11 +120,12 @@ nv84_fence_context_del(struct nouveau_channel *chan) nouveau_bo_vma_del(bo, &fctx->dispc_vma[i]); } + nouveau_bo_wr32(priv->bo, chan->chid * 16 / 4, fctx->base.sequence); nouveau_bo_vma_del(priv->bo, &fctx->vma_gart); nouveau_bo_vma_del(priv->bo, &fctx->vma); nouveau_fence_context_del(&fctx->base); chan->fence = NULL; - kfree(fctx); + nouveau_fence_context_free(&fctx->base); } int @@ -139,12 +140,13 @@ nv84_fence_context_new(struct nouveau_channel *chan) if (!fctx) return -ENOMEM; - nouveau_fence_context_new(&fctx->base); + nouveau_fence_context_new(chan, &fctx->base); fctx->base.emit = nv84_fence_emit; fctx->base.sync = nv84_fence_sync; fctx->base.read = nv84_fence_read; fctx->base.emit32 = nv84_fence_emit32; fctx->base.sync32 = nv84_fence_sync32; + fctx->base.sequence = nv84_fence_read(chan); ret = nouveau_bo_vma_add(priv->bo, cli->vm, &fctx->vma); if (ret == 0) { @@ -158,8 +160,6 @@ nv84_fence_context_new(struct nouveau_channel *chan) ret = nouveau_bo_vma_add(bo, cli->vm, &fctx->dispc_vma[i]); } - nouveau_bo_wr32(priv->bo, chan->chid * 16/4, 0x00000000); - if (ret) nv84_fence_context_del(chan); return ret; @@ -168,13 +168,12 @@ nv84_fence_context_new(struct nouveau_channel *chan) static bool nv84_fence_suspend(struct nouveau_drm *drm) { - struct nouveau_fifo *pfifo = nvkm_fifo(&drm->device); struct nv84_fence_priv *priv = drm->fence; int i; - priv->suspend = vmalloc((pfifo->max + 1) * sizeof(u32)); + priv->suspend = vmalloc(priv->base.contexts * sizeof(u32)); if (priv->suspend) { - for (i = 0; i <= pfifo->max; i++) + for (i = 0; i < priv->base.contexts; i++) priv->suspend[i] = nouveau_bo_rd32(priv->bo, i*4); } @@ -184,12 +183,11 @@ nv84_fence_suspend(struct nouveau_drm *drm) static void nv84_fence_resume(struct nouveau_drm *drm) { - struct nouveau_fifo *pfifo = nvkm_fifo(&drm->device); struct nv84_fence_priv *priv = drm->fence; int i; if (priv->suspend) { - for (i = 0; i <= pfifo->max; i++) + for (i = 0; i < priv->base.contexts; i++) nouveau_bo_wr32(priv->bo, i*4, priv->suspend[i]); vfree(priv->suspend); priv->suspend = NULL; @@ -229,11 +227,12 @@ nv84_fence_create(struct nouveau_drm *drm) priv->base.context_new = nv84_fence_context_new; priv->base.context_del = nv84_fence_context_del; - init_waitqueue_head(&priv->base.waiting); + priv->base.contexts = pfifo->max + 1; + priv->base.context_base = fence_context_alloc(priv->base.contexts); priv->base.uevent = true; - ret = nouveau_bo_new(drm->dev, 16 * (pfifo->max + 1), 0, - TTM_PL_FLAG_VRAM, 0, 0, NULL, &priv->bo); + ret = nouveau_bo_new(drm->dev, 16 * priv->base.contexts, 0, + TTM_PL_FLAG_VRAM, 0, 0, NULL, NULL, &priv->bo); if (ret == 0) { ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM); if (ret == 0) { @@ -246,8 +245,8 @@ nv84_fence_create(struct nouveau_drm *drm) } if (ret == 0) - ret = nouveau_bo_new(drm->dev, 16 * (pfifo->max + 1), 0, - TTM_PL_FLAG_TT, 0, 0, NULL, + ret = nouveau_bo_new(drm->dev, 16 * priv->base.contexts, 0, + TTM_PL_FLAG_TT, 0, 0, NULL, NULL, &priv->bo_gart); if (ret == 0) { ret = nouveau_bo_pin(priv->bo_gart, TTM_PL_FLAG_TT); diff --git a/drivers/gpu/drm/nouveau/nvif/class.h b/drivers/gpu/drm/nouveau/nvif/class.h index 573491f847921bfc6eec27245b370096664de122..e5a27df0672b5db5d0b21c11f8e46344eb706fa4 100644 --- a/drivers/gpu/drm/nouveau/nvif/class.h +++ b/drivers/gpu/drm/nouveau/nvif/class.h @@ -479,6 +479,8 @@ struct nv50_disp_core_channel_dma_v0 { __u32 pushbuf; }; +#define NV50_DISP_CORE_CHANNEL_DMA_V0_NTFY_UEVENT 0x00 + /* cursor immediate */ struct nv50_disp_cursor_v0 { __u8 version; @@ -486,6 +488,8 @@ struct nv50_disp_cursor_v0 { __u8 pad02[6]; }; +#define NV50_DISP_CURSOR_V0_NTFY_UEVENT 0x00 + /* base */ struct nv50_disp_base_channel_dma_v0 { __u8 version; @@ -494,6 +498,8 @@ struct nv50_disp_base_channel_dma_v0 { __u32 pushbuf; }; +#define NV50_DISP_BASE_CHANNEL_DMA_V0_NTFY_UEVENT 0x00 + /* overlay */ struct nv50_disp_overlay_channel_dma_v0 { __u8 version; @@ -502,6 +508,8 @@ struct nv50_disp_overlay_channel_dma_v0 { __u32 pushbuf; }; +#define NV50_DISP_OVERLAY_CHANNEL_DMA_V0_NTFY_UEVENT 0x00 + /* overlay immediate */ struct nv50_disp_overlay_v0 { __u8 version; @@ -509,6 +517,7 @@ struct nv50_disp_overlay_v0 { __u8 pad02[6]; }; +#define NV50_DISP_OVERLAY_V0_NTFY_UEVENT 0x00 /******************************************************************************* * fermi diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 002b9721e85a348077f09285c99bbb7600ecce32..862ba03c236ca1c42766cd8f94d4f4b93ceb5cdc 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -629,6 +629,7 @@ static struct drm_driver omap_drm_driver = { .lastclose = dev_lastclose, .preclose = dev_preclose, .postclose = dev_postclose, + .set_busid = drm_platform_set_busid, .get_vblank_counter = drm_vblank_count, .enable_vblank = omap_irq_enable_vblank, .disable_vblank = omap_irq_disable_vblank, diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 84d73a61b34b7fad41973f8a0f3051b799928c87..60e47b33c8017a788669bd604795438d63794648 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -26,6 +26,7 @@ #include #include #include +#include #include diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 4ce1db0a68ff5754c37cb1eace4edccc6d6f03db..23de22f8c82078a11a4c9a7815c8aa099d74ddf9 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -352,6 +352,30 @@ static const struct panel_desc auo_b101aw03 = { }, }; +static const struct drm_display_mode auo_b101xtn01_mode = { + .clock = 72000, + .hdisplay = 1366, + .hsync_start = 1366 + 20, + .hsync_end = 1366 + 20 + 70, + .htotal = 1366 + 20 + 70, + .vdisplay = 768, + .vsync_start = 768 + 14, + .vsync_end = 768 + 14 + 42, + .vtotal = 768 + 14 + 42, + .vrefresh = 60, + .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, +}; + +static const struct panel_desc auo_b101xtn01 = { + .modes = &auo_b101xtn01_mode, + .num_modes = 1, + .bpc = 6, + .size = { + .width = 223, + .height = 125, + }, +}; + static const struct drm_display_mode auo_b133xtn01_mode = { .clock = 69500, .hdisplay = 1366, @@ -615,6 +639,9 @@ static const struct of_device_id platform_of_match[] = { { .compatible = "auo,b101aw03", .data = &auo_b101aw03, + }, { + .compatible = "auo,b101xtn01", + .data = &auo_b101xtn01, }, { .compatible = "auo,b133htn01", .data = &auo_b133htn01, diff --git a/drivers/gpu/drm/qxl/Makefile b/drivers/gpu/drm/qxl/Makefile index ea046ba691d2efd8aa8083fc9a23f7aa54fcc399..bacc4aff120107c6f69610f4845502decd5d87c8 100644 --- a/drivers/gpu/drm/qxl/Makefile +++ b/drivers/gpu/drm/qxl/Makefile @@ -4,6 +4,6 @@ ccflags-y := -Iinclude/drm -qxl-y := qxl_drv.o qxl_kms.o qxl_display.o qxl_ttm.o qxl_fb.o qxl_object.o qxl_gem.o qxl_cmd.o qxl_image.o qxl_draw.o qxl_debugfs.o qxl_irq.o qxl_dumb.o qxl_ioctl.o qxl_fence.o qxl_release.o +qxl-y := qxl_drv.o qxl_kms.o qxl_display.o qxl_ttm.o qxl_fb.o qxl_object.o qxl_gem.o qxl_cmd.o qxl_image.o qxl_draw.o qxl_debugfs.o qxl_irq.o qxl_dumb.o qxl_ioctl.o qxl_release.o qxl_prime.o obj-$(CONFIG_DRM_QXL)+= qxl.o diff --git a/drivers/gpu/drm/qxl/qxl_cmd.c b/drivers/gpu/drm/qxl/qxl_cmd.c index eb89653a7a1710adf2842a2d2fb811cbe6c62737..97823644d34743a285d025c810a5168ed8c8f142 100644 --- a/drivers/gpu/drm/qxl/qxl_cmd.c +++ b/drivers/gpu/drm/qxl/qxl_cmd.c @@ -620,17 +620,10 @@ static int qxl_reap_surf(struct qxl_device *qdev, struct qxl_bo *surf, bool stal if (ret == -EBUSY) return -EBUSY; - if (surf->fence.num_active_releases > 0 && stall == false) { - qxl_bo_unreserve(surf); - return -EBUSY; - } - if (stall) mutex_unlock(&qdev->surf_evict_mutex); - spin_lock(&surf->tbo.bdev->fence_lock); ret = ttm_bo_wait(&surf->tbo, true, true, !stall); - spin_unlock(&surf->tbo.bdev->fence_lock); if (stall) mutex_lock(&qdev->surf_evict_mutex); diff --git a/drivers/gpu/drm/qxl/qxl_debugfs.c b/drivers/gpu/drm/qxl/qxl_debugfs.c index c3c2bbdc667496ef61cc9878e64544df0efd403a..6911b8c444928fdaa04f2f2d82b1180def5aba09 100644 --- a/drivers/gpu/drm/qxl/qxl_debugfs.c +++ b/drivers/gpu/drm/qxl/qxl_debugfs.c @@ -58,9 +58,17 @@ qxl_debugfs_buffers_info(struct seq_file *m, void *data) struct qxl_bo *bo; list_for_each_entry(bo, &qdev->gem.objects, list) { - seq_printf(m, "size %ld, pc %d, sync obj %p, num releases %d\n", - (unsigned long)bo->gem_base.size, bo->pin_count, - bo->tbo.sync_obj, bo->fence.num_active_releases); + struct reservation_object_list *fobj; + int rel; + + rcu_read_lock(); + fobj = rcu_dereference(bo->tbo.resv->fence); + rel = fobj ? fobj->shared_count : 0; + rcu_read_unlock(); + + seq_printf(m, "size %ld, pc %d, num releases %d\n", + (unsigned long)bo->gem_base.size, + bo->pin_count, rel); } return 0; } diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index b8ced08b62916b643436375aa853a82c2560a947..af9e785466883ae52a9812620421221af0630894 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -187,6 +187,54 @@ static void qxl_crtc_destroy(struct drm_crtc *crtc) kfree(qxl_crtc); } +static int qxl_crtc_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t page_flip_flags) +{ + struct drm_device *dev = crtc->dev; + struct qxl_device *qdev = dev->dev_private; + struct qxl_crtc *qcrtc = to_qxl_crtc(crtc); + struct qxl_framebuffer *qfb_src = to_qxl_framebuffer(fb); + struct qxl_framebuffer *qfb_old = to_qxl_framebuffer(crtc->primary->fb); + struct qxl_bo *bo_old = gem_to_qxl_bo(qfb_old->obj); + struct qxl_bo *bo = gem_to_qxl_bo(qfb_src->obj); + unsigned long flags; + struct drm_clip_rect norect = { + .x1 = 0, + .y1 = 0, + .x2 = fb->width, + .y2 = fb->height + }; + int inc = 1; + int one_clip_rect = 1; + int ret = 0; + + crtc->primary->fb = fb; + bo_old->is_primary = false; + bo->is_primary = true; + + ret = qxl_bo_reserve(bo, false); + if (ret) + return ret; + + qxl_draw_dirty_fb(qdev, qfb_src, bo, 0, 0, + &norect, one_clip_rect, inc); + + drm_vblank_get(dev, qcrtc->index); + + if (event) { + spin_lock_irqsave(&dev->event_lock, flags); + drm_send_vblank_event(dev, qcrtc->index, event); + spin_unlock_irqrestore(&dev->event_lock, flags); + } + drm_vblank_put(dev, qcrtc->index); + + qxl_bo_unreserve(bo); + + return 0; +} + static int qxl_hide_cursor(struct qxl_device *qdev) { @@ -374,6 +422,7 @@ static const struct drm_crtc_funcs qxl_crtc_funcs = { .cursor_move = qxl_crtc_cursor_move, .set_config = drm_crtc_helper_set_config, .destroy = qxl_crtc_destroy, + .page_flip = qxl_crtc_page_flip, }; static void qxl_user_framebuffer_destroy(struct drm_framebuffer *fb) diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index a3fd92029a14b6050e8f91cbeeb9a735bc6f3299..1d9b80c91a152585ba8c4380825e5964fe5e4cf0 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -84,6 +84,7 @@ static const struct file_operations qxl_fops = { .release = drm_release, .unlocked_ioctl = drm_ioctl, .poll = drm_poll, + .read = drm_read, .mmap = qxl_mmap, }; @@ -195,6 +196,20 @@ static int qxl_pm_restore(struct device *dev) return qxl_drm_resume(drm_dev, false); } +static u32 qxl_noop_get_vblank_counter(struct drm_device *dev, int crtc) +{ + return dev->vblank[crtc].count.counter; +} + +static int qxl_noop_enable_vblank(struct drm_device *dev, int crtc) +{ + return 0; +} + +static void qxl_noop_disable_vblank(struct drm_device *dev, int crtc) +{ +} + static const struct dev_pm_ops qxl_pm_ops = { .suspend = qxl_pm_suspend, .resume = qxl_pm_resume, @@ -212,10 +227,15 @@ static struct pci_driver qxl_pci_driver = { }; static struct drm_driver qxl_driver = { - .driver_features = DRIVER_GEM | DRIVER_MODESET | + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, .load = qxl_driver_load, .unload = qxl_driver_unload, + .get_vblank_counter = qxl_noop_get_vblank_counter, + .enable_vblank = qxl_noop_enable_vblank, + .disable_vblank = qxl_noop_disable_vblank, + + .set_busid = drm_pci_set_busid, .dumb_create = qxl_mode_dumb_create, .dumb_map_offset = qxl_mode_dumb_mmap, @@ -224,6 +244,17 @@ static struct drm_driver qxl_driver = { .debugfs_init = qxl_debugfs_init, .debugfs_cleanup = qxl_debugfs_takedown, #endif + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_pin = qxl_gem_prime_pin, + .gem_prime_unpin = qxl_gem_prime_unpin, + .gem_prime_get_sg_table = qxl_gem_prime_get_sg_table, + .gem_prime_import_sg_table = qxl_gem_prime_import_sg_table, + .gem_prime_vmap = qxl_gem_prime_vmap, + .gem_prime_vunmap = qxl_gem_prime_vunmap, + .gem_prime_mmap = qxl_gem_prime_mmap, .gem_free_object = qxl_gem_object_free, .gem_open_object = qxl_gem_object_open, .gem_close_object = qxl_gem_object_close, diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index 36ed40ba773f8149d88eacef4c0633614e09eb83..7c6cafe21f5f5fd2b354c8d34494fff51fc84d3b 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -31,6 +31,7 @@ * Definitions taken from spice-protocol, plus kernel driver specific bits. */ +#include #include #include #include @@ -42,6 +43,8 @@ #include #include +#include + /* just for ttm_validate_buffer */ #include @@ -95,31 +98,24 @@ enum { QXL_INTERRUPT_IO_CMD |\ QXL_INTERRUPT_CLIENT_MONITORS_CONFIG) -struct qxl_fence { - struct qxl_device *qdev; - uint32_t num_active_releases; - uint32_t *release_ids; - struct radix_tree_root tree; -}; - struct qxl_bo { /* Protected by gem.mutex */ struct list_head list; /* Protected by tbo.reserved */ - u32 placements[3]; + struct ttm_place placements[3]; struct ttm_placement placement; struct ttm_buffer_object tbo; struct ttm_bo_kmap_obj kmap; unsigned pin_count; void *kptr; int type; + /* Constant after initialization */ struct drm_gem_object gem_base; bool is_primary; /* is this now a primary surface */ bool hw_surf_alloc; struct qxl_surface surf; uint32_t surface_id; - struct qxl_fence fence; /* per bo fence - list of releases */ struct qxl_release *surf_create; }; #define gem_to_qxl_bo(gobj) container_of((gobj), struct qxl_bo, gem_base) @@ -191,6 +187,8 @@ enum { * spice-protocol/qxl_dev.h */ #define QXL_MAX_RES 96 struct qxl_release { + struct fence base; + int id; int type; uint32_t release_offset; @@ -284,7 +282,9 @@ struct qxl_device { uint8_t slot_gen_bits; uint64_t va_slot_mask; + spinlock_t release_lock; struct idr release_idr; + uint32_t release_seqno; spinlock_t release_idr_lock; struct mutex async_io_mutex; unsigned int last_sent_io_cmd; @@ -532,6 +532,18 @@ int qxl_garbage_collect(struct qxl_device *qdev); int qxl_debugfs_init(struct drm_minor *minor); void qxl_debugfs_takedown(struct drm_minor *minor); +/* qxl_prime.c */ +int qxl_gem_prime_pin(struct drm_gem_object *obj); +void qxl_gem_prime_unpin(struct drm_gem_object *obj); +struct sg_table *qxl_gem_prime_get_sg_table(struct drm_gem_object *obj); +struct drm_gem_object *qxl_gem_prime_import_sg_table( + struct drm_device *dev, struct dma_buf_attachment *attach, + struct sg_table *sgt); +void *qxl_gem_prime_vmap(struct drm_gem_object *obj); +void qxl_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); +int qxl_gem_prime_mmap(struct drm_gem_object *obj, + struct vm_area_struct *vma); + /* qxl_irq.c */ int qxl_irq_init(struct qxl_device *qdev); irqreturn_t qxl_irq_handler(int irq, void *arg); @@ -561,10 +573,4 @@ qxl_surface_lookup(struct drm_device *dev, int surface_id); void qxl_surface_evict(struct qxl_device *qdev, struct qxl_bo *surf, bool freeing); int qxl_update_surface(struct qxl_device *qdev, struct qxl_bo *surf); -/* qxl_fence.c */ -void qxl_fence_add_release_locked(struct qxl_fence *qfence, uint32_t rel_id); -int qxl_fence_remove_release(struct qxl_fence *qfence, uint32_t rel_id); -int qxl_fence_init(struct qxl_device *qdev, struct qxl_fence *qfence); -void qxl_fence_fini(struct qxl_fence *qfence); - #endif diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c index df567888bb1e3e04ef73b3b3f38a8a9e75e1fc5d..3d7c1d00a424cfbdbdf53881421f2f4e193f247e 100644 --- a/drivers/gpu/drm/qxl/qxl_fb.c +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -625,7 +625,8 @@ static int qxl_fb_find_or_create_single( struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { - struct qxl_fbdev *qfbdev = (struct qxl_fbdev *)helper; + struct qxl_fbdev *qfbdev = + container_of(helper, struct qxl_fbdev, helper); int new_fb = 0; int ret; diff --git a/drivers/gpu/drm/qxl/qxl_fence.c b/drivers/gpu/drm/qxl/qxl_fence.c deleted file mode 100644 index ae59e91cfb9ab1694aee3047e094166130e7b123..0000000000000000000000000000000000000000 --- a/drivers/gpu/drm/qxl/qxl_fence.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2013 Red Hat Inc. - * - * 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. - * - * Authors: Dave Airlie - * Alon Levy - */ - - -#include "qxl_drv.h" - -/* QXL fencing- - - When we submit operations to the GPU we pass a release reference to the GPU - with them, the release reference is then added to the release ring when - the GPU is finished with that particular operation and has removed it from - its tree. - - So we have can have multiple outstanding non linear fences per object. - - From a TTM POV we only care if the object has any outstanding releases on - it. - - we wait until all outstanding releases are processeed. - - sync object is just a list of release ids that represent that fence on - that buffer. - - we just add new releases onto the sync object attached to the object. - - This currently uses a radix tree to store the list of release ids. - - For some reason every so often qxl hw fails to release, things go wrong. -*/ -/* must be called with the fence lock held */ -void qxl_fence_add_release_locked(struct qxl_fence *qfence, uint32_t rel_id) -{ - radix_tree_insert(&qfence->tree, rel_id, qfence); - qfence->num_active_releases++; -} - -int qxl_fence_remove_release(struct qxl_fence *qfence, uint32_t rel_id) -{ - void *ret; - int retval = 0; - struct qxl_bo *bo = container_of(qfence, struct qxl_bo, fence); - - spin_lock(&bo->tbo.bdev->fence_lock); - - ret = radix_tree_delete(&qfence->tree, rel_id); - if (ret == qfence) - qfence->num_active_releases--; - else { - DRM_DEBUG("didn't find fence in radix tree for %d\n", rel_id); - retval = -ENOENT; - } - spin_unlock(&bo->tbo.bdev->fence_lock); - return retval; -} - - -int qxl_fence_init(struct qxl_device *qdev, struct qxl_fence *qfence) -{ - qfence->qdev = qdev; - qfence->num_active_releases = 0; - INIT_RADIX_TREE(&qfence->tree, GFP_ATOMIC); - return 0; -} - -void qxl_fence_fini(struct qxl_fence *qfence) -{ - kfree(qfence->release_ids); - qfence->num_active_releases = 0; -} diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c index fd88eb4a3f79d1be5b5478ef04a3c77dde2dbb71..b2977a18193589250f1c0e69814eb72ec05ef05e 100644 --- a/drivers/gpu/drm/qxl/qxl_kms.c +++ b/drivers/gpu/drm/qxl/qxl_kms.c @@ -223,6 +223,7 @@ static int qxl_device_init(struct qxl_device *qdev, idr_init(&qdev->release_idr); spin_lock_init(&qdev->release_idr_lock); + spin_lock_init(&qdev->release_lock); idr_init(&qdev->surf_id_idr); spin_lock_init(&qdev->surf_id_idr_lock); @@ -297,6 +298,9 @@ int qxl_driver_unload(struct drm_device *dev) if (qdev == NULL) return 0; + + drm_vblank_cleanup(dev); + qxl_modeset_fini(qdev); qxl_device_fini(qdev); @@ -324,15 +328,20 @@ int qxl_driver_load(struct drm_device *dev, unsigned long flags) if (r) goto out; + r = drm_vblank_init(dev, 1); + if (r) + goto unload; + r = qxl_modeset_init(qdev); - if (r) { - qxl_driver_unload(dev); - goto out; - } + if (r) + goto unload; drm_kms_helper_poll_init(qdev->ddev); return 0; +unload: + qxl_driver_unload(dev); + out: kfree(qdev); return r; diff --git a/drivers/gpu/drm/qxl/qxl_object.c b/drivers/gpu/drm/qxl/qxl_object.c index b95f144f0b4934abbb034e22c5d057a065bcecd6..cdeaf08fdc745a9b27600e78edc200e97e53c620 100644 --- a/drivers/gpu/drm/qxl/qxl_object.c +++ b/drivers/gpu/drm/qxl/qxl_object.c @@ -36,7 +36,6 @@ static void qxl_ttm_bo_destroy(struct ttm_buffer_object *tbo) qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; qxl_surface_evict(qdev, bo, false); - qxl_fence_fini(&bo->fence); mutex_lock(&qdev->gem.mutex); list_del_init(&bo->list); mutex_unlock(&qdev->gem.mutex); @@ -55,21 +54,24 @@ void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain, bool pinned) { u32 c = 0; u32 pflag = pinned ? TTM_PL_FLAG_NO_EVICT : 0; + unsigned i; - qbo->placement.fpfn = 0; - qbo->placement.lpfn = 0; qbo->placement.placement = qbo->placements; qbo->placement.busy_placement = qbo->placements; if (domain == QXL_GEM_DOMAIN_VRAM) - qbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_VRAM | pflag; + qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_VRAM | pflag; if (domain == QXL_GEM_DOMAIN_SURFACE) - qbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_PRIV0 | pflag; + qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_PRIV0 | pflag; if (domain == QXL_GEM_DOMAIN_CPU) - qbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM | pflag; + qbo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM | pflag; if (!c) - qbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + qbo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; qbo->placement.num_placement = c; qbo->placement.num_busy_placement = c; + for (i = 0; i < c; ++i) { + qbo->placements[i].fpfn = 0; + qbo->placements[i].lpfn = 0; + } } @@ -99,7 +101,6 @@ int qxl_bo_create(struct qxl_device *qdev, bo->type = domain; bo->pin_count = pinned ? 1 : 0; bo->surface_id = 0; - qxl_fence_init(qdev, &bo->fence); INIT_LIST_HEAD(&bo->list); if (surf) @@ -109,7 +110,7 @@ int qxl_bo_create(struct qxl_device *qdev, r = ttm_bo_init(&qdev->mman.bdev, &bo->tbo, size, type, &bo->placement, 0, !kernel, NULL, size, - NULL, &qxl_ttm_bo_destroy); + NULL, NULL, &qxl_ttm_bo_destroy); if (unlikely(r != 0)) { if (r != -ERESTARTSYS) dev_err(qdev->dev, @@ -259,7 +260,7 @@ int qxl_bo_unpin(struct qxl_bo *bo) if (bo->pin_count) return 0; for (i = 0; i < bo->placement.num_placement; i++) - bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT; + bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); if (unlikely(r != 0)) dev_err(qdev->dev, "%p validate failed for unpin\n", bo); diff --git a/drivers/gpu/drm/qxl/qxl_object.h b/drivers/gpu/drm/qxl/qxl_object.h index 83a423293afd67a66079dac61ec8bbc48f254edb..37af1bc0dd00102431e1f9415e0af7cf39b4edf8 100644 --- a/drivers/gpu/drm/qxl/qxl_object.h +++ b/drivers/gpu/drm/qxl/qxl_object.h @@ -76,12 +76,10 @@ static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type, } return r; } - spin_lock(&bo->tbo.bdev->fence_lock); if (mem_type) *mem_type = bo->tbo.mem.mem_type; - if (bo->tbo.sync_obj) - r = ttm_bo_wait(&bo->tbo, true, true, no_wait); - spin_unlock(&bo->tbo.bdev->fence_lock); + + r = ttm_bo_wait(&bo->tbo, true, true, no_wait); ttm_bo_unreserve(&bo->tbo); return r; } diff --git a/drivers/gpu/drm/qxl/qxl_prime.c b/drivers/gpu/drm/qxl/qxl_prime.c new file mode 100644 index 0000000000000000000000000000000000000000..3d031b50a8fd13dd8203f4de284ecedbb95ecbcc --- /dev/null +++ b/drivers/gpu/drm/qxl/qxl_prime.c @@ -0,0 +1,72 @@ +/* + * Copyright 2014 Canonical + * + * 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. + * + * Authors: Andreas Pokorny + */ + +#include "qxl_drv.h" + +/* Empty Implementations as there should not be any other driver for a virtual + * device that might share buffers with qxl */ + +int qxl_gem_prime_pin(struct drm_gem_object *obj) +{ + WARN_ONCE(1, "not implemented"); + return -ENOSYS; +} + +void qxl_gem_prime_unpin(struct drm_gem_object *obj) +{ + WARN_ONCE(1, "not implemented"); +} + + +struct sg_table *qxl_gem_prime_get_sg_table(struct drm_gem_object *obj) +{ + WARN_ONCE(1, "not implemented"); + return ERR_PTR(-ENOSYS); +} + +struct drm_gem_object *qxl_gem_prime_import_sg_table( + struct drm_device *dev, struct dma_buf_attachment *attach, + struct sg_table *table) +{ + WARN_ONCE(1, "not implemented"); + return ERR_PTR(-ENOSYS); +} + +void *qxl_gem_prime_vmap(struct drm_gem_object *obj) +{ + WARN_ONCE(1, "not implemented"); + return ERR_PTR(-ENOSYS); +} + +void qxl_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) +{ + WARN_ONCE(1, "not implemented"); +} + +int qxl_gem_prime_mmap(struct drm_gem_object *obj, + struct vm_area_struct *area) +{ + WARN_ONCE(1, "not implemented"); + return ENOSYS; +} diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c index 14e776f1d14e3bf099259d8d6b8f94ac9896f63b..446e71ca36cb111c5d47aad5b4928eb6548a68c5 100644 --- a/drivers/gpu/drm/qxl/qxl_release.c +++ b/drivers/gpu/drm/qxl/qxl_release.c @@ -21,6 +21,7 @@ */ #include "qxl_drv.h" #include "qxl_object.h" +#include /* * drawable cmd cache - allocate a bunch of VRAM pages, suballocate @@ -39,6 +40,88 @@ static const int release_size_per_bo[] = { RELEASE_SIZE, SURFACE_RELEASE_SIZE, RELEASE_SIZE }; static const int releases_per_bo[] = { RELEASES_PER_BO, SURFACE_RELEASES_PER_BO, RELEASES_PER_BO }; +static const char *qxl_get_driver_name(struct fence *fence) +{ + return "qxl"; +} + +static const char *qxl_get_timeline_name(struct fence *fence) +{ + return "release"; +} + +static bool qxl_nop_signaling(struct fence *fence) +{ + /* fences are always automatically signaled, so just pretend we did this.. */ + return true; +} + +static long qxl_fence_wait(struct fence *fence, bool intr, signed long timeout) +{ + struct qxl_device *qdev; + struct qxl_release *release; + int count = 0, sc = 0; + bool have_drawable_releases; + unsigned long cur, end = jiffies + timeout; + + qdev = container_of(fence->lock, struct qxl_device, release_lock); + release = container_of(fence, struct qxl_release, base); + have_drawable_releases = release->type == QXL_RELEASE_DRAWABLE; + +retry: + sc++; + + if (fence_is_signaled(fence)) + goto signaled; + + qxl_io_notify_oom(qdev); + + for (count = 0; count < 11; count++) { + if (!qxl_queue_garbage_collect(qdev, true)) + break; + + if (fence_is_signaled(fence)) + goto signaled; + } + + if (fence_is_signaled(fence)) + goto signaled; + + if (have_drawable_releases || sc < 4) { + if (sc > 2) + /* back off */ + usleep_range(500, 1000); + + if (time_after(jiffies, end)) + return 0; + + if (have_drawable_releases && sc > 300) { + FENCE_WARN(fence, "failed to wait on release %d " + "after spincount %d\n", + fence->context & ~0xf0000000, sc); + goto signaled; + } + goto retry; + } + /* + * yeah, original sync_obj_wait gave up after 3 spins when + * have_drawable_releases is not set. + */ + +signaled: + cur = jiffies; + if (time_after(cur, end)) + return 0; + return end - cur; +} + +static const struct fence_ops qxl_fence_ops = { + .get_driver_name = qxl_get_driver_name, + .get_timeline_name = qxl_get_timeline_name, + .enable_signaling = qxl_nop_signaling, + .wait = qxl_fence_wait, +}; + static uint64_t qxl_release_alloc(struct qxl_device *qdev, int type, struct qxl_release **ret) @@ -46,13 +129,13 @@ qxl_release_alloc(struct qxl_device *qdev, int type, struct qxl_release *release; int handle; size_t size = sizeof(*release); - int idr_ret; release = kmalloc(size, GFP_KERNEL); if (!release) { DRM_ERROR("Out of memory\n"); return 0; } + release->base.ops = NULL; release->type = type; release->release_offset = 0; release->surface_release_id = 0; @@ -60,44 +143,61 @@ qxl_release_alloc(struct qxl_device *qdev, int type, idr_preload(GFP_KERNEL); spin_lock(&qdev->release_idr_lock); - idr_ret = idr_alloc(&qdev->release_idr, release, 1, 0, GFP_NOWAIT); + handle = idr_alloc(&qdev->release_idr, release, 1, 0, GFP_NOWAIT); + release->base.seqno = ++qdev->release_seqno; spin_unlock(&qdev->release_idr_lock); idr_preload_end(); - handle = idr_ret; - if (idr_ret < 0) - goto release_fail; + if (handle < 0) { + kfree(release); + *ret = NULL; + return handle; + } *ret = release; QXL_INFO(qdev, "allocated release %lld\n", handle); release->id = handle; -release_fail: - return handle; } +static void +qxl_release_free_list(struct qxl_release *release) +{ + while (!list_empty(&release->bos)) { + struct qxl_bo_list *entry; + struct qxl_bo *bo; + + entry = container_of(release->bos.next, + struct qxl_bo_list, tv.head); + bo = to_qxl_bo(entry->tv.bo); + qxl_bo_unref(&bo); + list_del(&entry->tv.head); + kfree(entry); + } +} + void qxl_release_free(struct qxl_device *qdev, struct qxl_release *release) { - struct qxl_bo_list *entry, *tmp; QXL_INFO(qdev, "release %d, type %d\n", release->id, release->type); if (release->surface_release_id) qxl_surface_id_dealloc(qdev, release->surface_release_id); - list_for_each_entry_safe(entry, tmp, &release->bos, tv.head) { - struct qxl_bo *bo = to_qxl_bo(entry->tv.bo); - QXL_INFO(qdev, "release %llx\n", - drm_vma_node_offset_addr(&entry->tv.bo->vma_node) - - DRM_FILE_OFFSET); - qxl_fence_remove_release(&bo->fence, release->id); - qxl_bo_unref(&bo); - kfree(entry); - } spin_lock(&qdev->release_idr_lock); idr_remove(&qdev->release_idr, release->id); spin_unlock(&qdev->release_idr_lock); - kfree(release); + + if (release->base.ops) { + WARN_ON(list_empty(&release->bos)); + qxl_release_free_list(release); + + fence_signal(&release->base); + fence_put(&release->base); + } else { + qxl_release_free_list(release); + kfree(release); + } } static int qxl_release_bo_alloc(struct qxl_device *qdev, @@ -126,6 +226,7 @@ int qxl_release_list_add(struct qxl_release *release, struct qxl_bo *bo) qxl_bo_ref(bo); entry->tv.bo = &bo->tbo; + entry->tv.shared = false; list_add_tail(&entry->tv.head, &release->bos); return 0; } @@ -142,6 +243,10 @@ static int qxl_release_validate_bo(struct qxl_bo *bo) return ret; } + ret = reservation_object_reserve_shared(bo->tbo.resv); + if (ret) + return ret; + /* allocate a surface for reserved + validated buffers */ ret = qxl_bo_check_id(bo->gem_base.dev->dev_private, bo); if (ret) @@ -159,7 +264,7 @@ int qxl_release_reserve_list(struct qxl_release *release, bool no_intr) if (list_is_singular(&release->bos)) return 0; - ret = ttm_eu_reserve_buffers(&release->ticket, &release->bos); + ret = ttm_eu_reserve_buffers(&release->ticket, &release->bos, !no_intr); if (ret) return ret; @@ -199,6 +304,8 @@ int qxl_alloc_surface_release_reserved(struct qxl_device *qdev, /* stash the release after the create command */ idr_ret = qxl_release_alloc(qdev, QXL_RELEASE_SURFACE_CMD, release); + if (idr_ret < 0) + return idr_ret; bo = qxl_bo_ref(to_qxl_bo(entry->tv.bo)); (*release)->release_offset = create_rel->release_offset + 64; @@ -239,6 +346,11 @@ int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size, } idr_ret = qxl_release_alloc(qdev, type, release); + if (idr_ret < 0) { + if (rbo) + *rbo = NULL; + return idr_ret; + } mutex_lock(&qdev->release_mutex); if (qdev->current_release_bo_offset[cur_idx] + 1 >= releases_per_bo[cur_idx]) { @@ -319,40 +431,44 @@ void qxl_release_unmap(struct qxl_device *qdev, void qxl_release_fence_buffer_objects(struct qxl_release *release) { - struct ttm_validate_buffer *entry; struct ttm_buffer_object *bo; struct ttm_bo_global *glob; struct ttm_bo_device *bdev; struct ttm_bo_driver *driver; struct qxl_bo *qbo; + struct ttm_validate_buffer *entry; + struct qxl_device *qdev; /* if only one object on the release its the release itself since these objects are pinned no need to reserve */ - if (list_is_singular(&release->bos)) + if (list_is_singular(&release->bos) || list_empty(&release->bos)) return; bo = list_first_entry(&release->bos, struct ttm_validate_buffer, head)->bo; bdev = bo->bdev; + qdev = container_of(bdev, struct qxl_device, mman.bdev); + + /* + * Since we never really allocated a context and we don't want to conflict, + * set the highest bits. This will break if we really allow exporting of dma-bufs. + */ + fence_init(&release->base, &qxl_fence_ops, &qdev->release_lock, + release->id | 0xf0000000, release->base.seqno); + trace_fence_emit(&release->base); + driver = bdev->driver; glob = bo->glob; spin_lock(&glob->lru_lock); - spin_lock(&bdev->fence_lock); list_for_each_entry(entry, &release->bos, head) { bo = entry->bo; qbo = to_qxl_bo(bo); - if (!entry->bo->sync_obj) - entry->bo->sync_obj = &qbo->fence; - - qxl_fence_add_release_locked(&qbo->fence, release->id); - + reservation_object_add_shared_fence(bo->resv, &release->base); ttm_bo_add_to_lru(bo); __ttm_bo_unreserve(bo); - entry->reserved = false; } - spin_unlock(&bdev->fence_lock); spin_unlock(&glob->lru_lock); ww_acquire_fini(&release->ticket); } diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c index 71a1baeac14edc4ceabb8ab397d0dfe2c23b335b..0cbc4c9871643eb5da5cf210fc8b301dee912feb 100644 --- a/drivers/gpu/drm/qxl/qxl_ttm.c +++ b/drivers/gpu/drm/qxl/qxl_ttm.c @@ -127,7 +127,7 @@ int qxl_mmap(struct file *filp, struct vm_area_struct *vma) if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) { pr_info("%s: vma->vm_pgoff (%ld) < DRM_FILE_PAGE_OFFSET\n", __func__, vma->vm_pgoff); - return drm_mmap(filp, vma); + return -EINVAL; } file_priv = filp->private_data; @@ -188,11 +188,13 @@ static void qxl_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *placement) { struct qxl_bo *qbo; - static u32 placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + static struct ttm_place placements = { + .fpfn = 0, + .lpfn = 0, + .flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM + }; if (!qxl_ttm_bo_is_qxl_bo(bo)) { - placement->fpfn = 0; - placement->lpfn = 0; placement->placement = &placements; placement->busy_placement = &placements; placement->num_placement = 1; @@ -355,92 +357,6 @@ static int qxl_bo_move(struct ttm_buffer_object *bo, return ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem); } - -static int qxl_sync_obj_wait(void *sync_obj, - bool lazy, bool interruptible) -{ - struct qxl_fence *qfence = (struct qxl_fence *)sync_obj; - int count = 0, sc = 0; - struct qxl_bo *bo = container_of(qfence, struct qxl_bo, fence); - - if (qfence->num_active_releases == 0) - return 0; - -retry: - if (sc == 0) { - if (bo->type == QXL_GEM_DOMAIN_SURFACE) - qxl_update_surface(qfence->qdev, bo); - } else if (sc >= 1) { - qxl_io_notify_oom(qfence->qdev); - } - - sc++; - - for (count = 0; count < 10; count++) { - bool ret; - ret = qxl_queue_garbage_collect(qfence->qdev, true); - if (ret == false) - break; - - if (qfence->num_active_releases == 0) - return 0; - } - - if (qfence->num_active_releases) { - bool have_drawable_releases = false; - void **slot; - struct radix_tree_iter iter; - int release_id; - - radix_tree_for_each_slot(slot, &qfence->tree, &iter, 0) { - struct qxl_release *release; - - release_id = iter.index; - release = qxl_release_from_id_locked(qfence->qdev, release_id); - if (release == NULL) - continue; - - if (release->type == QXL_RELEASE_DRAWABLE) - have_drawable_releases = true; - } - - qxl_queue_garbage_collect(qfence->qdev, true); - - if (have_drawable_releases || sc < 4) { - if (sc > 2) - /* back off */ - usleep_range(500, 1000); - if (have_drawable_releases && sc > 300) { - WARN(1, "sync obj %d still has outstanding releases %d %d %d %ld %d\n", sc, bo->surface_id, bo->is_primary, bo->pin_count, (unsigned long)bo->gem_base.size, qfence->num_active_releases); - return -EBUSY; - } - goto retry; - } - } - return 0; -} - -static int qxl_sync_obj_flush(void *sync_obj) -{ - return 0; -} - -static void qxl_sync_obj_unref(void **sync_obj) -{ - *sync_obj = NULL; -} - -static void *qxl_sync_obj_ref(void *sync_obj) -{ - return sync_obj; -} - -static bool qxl_sync_obj_signaled(void *sync_obj) -{ - struct qxl_fence *qfence = (struct qxl_fence *)sync_obj; - return (qfence->num_active_releases == 0); -} - static void qxl_bo_move_notify(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem) { @@ -467,16 +383,9 @@ static struct ttm_bo_driver qxl_bo_driver = { .verify_access = &qxl_verify_access, .io_mem_reserve = &qxl_ttm_io_mem_reserve, .io_mem_free = &qxl_ttm_io_mem_free, - .sync_obj_signaled = &qxl_sync_obj_signaled, - .sync_obj_wait = &qxl_sync_obj_wait, - .sync_obj_flush = &qxl_sync_obj_flush, - .sync_obj_unref = &qxl_sync_obj_unref, - .sync_obj_ref = &qxl_sync_obj_ref, .move_notify = &qxl_bo_move_notify, }; - - int qxl_ttm_init(struct qxl_device *qdev) { int r; diff --git a/drivers/gpu/drm/r128/r128_cce.c b/drivers/gpu/drm/r128/r128_cce.c index 59459fe4e8c57b9c367887daa945324ac7f4eb34..2c45ac9c1dc3afc7d956387c8acd337797edacc9 100644 --- a/drivers/gpu/drm/r128/r128_cce.c +++ b/drivers/gpu/drm/r128/r128_cce.c @@ -452,7 +452,7 @@ static int r128_do_init_cce(struct drm_device *dev, drm_r128_init_t *init) dev_priv->span_pitch_offset_c = (((dev_priv->depth_pitch / 8) << 21) | (dev_priv->span_offset >> 5)); - dev_priv->sarea = drm_getsarea(dev); + dev_priv->sarea = drm_legacy_getsarea(dev); if (!dev_priv->sarea) { DRM_ERROR("could not find sarea!\n"); dev->dev_private = (void *)dev_priv; @@ -460,21 +460,21 @@ static int r128_do_init_cce(struct drm_device *dev, drm_r128_init_t *init) return -EINVAL; } - dev_priv->mmio = drm_core_findmap(dev, init->mmio_offset); + dev_priv->mmio = drm_legacy_findmap(dev, init->mmio_offset); if (!dev_priv->mmio) { DRM_ERROR("could not find mmio region!\n"); dev->dev_private = (void *)dev_priv; r128_do_cleanup_cce(dev); return -EINVAL; } - dev_priv->cce_ring = drm_core_findmap(dev, init->ring_offset); + dev_priv->cce_ring = drm_legacy_findmap(dev, init->ring_offset); if (!dev_priv->cce_ring) { DRM_ERROR("could not find cce ring region!\n"); dev->dev_private = (void *)dev_priv; r128_do_cleanup_cce(dev); return -EINVAL; } - dev_priv->ring_rptr = drm_core_findmap(dev, init->ring_rptr_offset); + dev_priv->ring_rptr = drm_legacy_findmap(dev, init->ring_rptr_offset); if (!dev_priv->ring_rptr) { DRM_ERROR("could not find ring read pointer!\n"); dev->dev_private = (void *)dev_priv; @@ -482,7 +482,7 @@ static int r128_do_init_cce(struct drm_device *dev, drm_r128_init_t *init) return -EINVAL; } dev->agp_buffer_token = init->buffers_offset; - dev->agp_buffer_map = drm_core_findmap(dev, init->buffers_offset); + dev->agp_buffer_map = drm_legacy_findmap(dev, init->buffers_offset); if (!dev->agp_buffer_map) { DRM_ERROR("could not find dma buffer region!\n"); dev->dev_private = (void *)dev_priv; @@ -492,7 +492,7 @@ static int r128_do_init_cce(struct drm_device *dev, drm_r128_init_t *init) if (!dev_priv->is_pci) { dev_priv->agp_textures = - drm_core_findmap(dev, init->agp_textures_offset); + drm_legacy_findmap(dev, init->agp_textures_offset); if (!dev_priv->agp_textures) { DRM_ERROR("could not find agp texture region!\n"); dev->dev_private = (void *)dev_priv; @@ -507,9 +507,9 @@ static int r128_do_init_cce(struct drm_device *dev, drm_r128_init_t *init) #if __OS_HAS_AGP if (!dev_priv->is_pci) { - drm_core_ioremap_wc(dev_priv->cce_ring, dev); - drm_core_ioremap_wc(dev_priv->ring_rptr, dev); - drm_core_ioremap_wc(dev->agp_buffer_map, dev); + drm_legacy_ioremap_wc(dev_priv->cce_ring, dev); + drm_legacy_ioremap_wc(dev_priv->ring_rptr, dev); + drm_legacy_ioremap_wc(dev->agp_buffer_map, dev); if (!dev_priv->cce_ring->handle || !dev_priv->ring_rptr->handle || !dev->agp_buffer_map->handle) { @@ -603,11 +603,11 @@ int r128_do_cleanup_cce(struct drm_device *dev) #if __OS_HAS_AGP if (!dev_priv->is_pci) { if (dev_priv->cce_ring != NULL) - drm_core_ioremapfree(dev_priv->cce_ring, dev); + drm_legacy_ioremapfree(dev_priv->cce_ring, dev); if (dev_priv->ring_rptr != NULL) - drm_core_ioremapfree(dev_priv->ring_rptr, dev); + drm_legacy_ioremapfree(dev_priv->ring_rptr, dev); if (dev->agp_buffer_map != NULL) { - drm_core_ioremapfree(dev->agp_buffer_map, dev); + drm_legacy_ioremapfree(dev->agp_buffer_map, dev); dev->agp_buffer_map = NULL; } } else diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c index 5bd307cd8da129316db121ad0e41736a10338322..c57b4de63caf5f633edda167a83009f6fb6a8484 100644 --- a/drivers/gpu/drm/r128/r128_drv.c +++ b/drivers/gpu/drm/r128/r128_drv.c @@ -46,7 +46,7 @@ static const struct file_operations r128_driver_fops = { .open = drm_open, .release = drm_release, .unlocked_ioctl = drm_ioctl, - .mmap = drm_mmap, + .mmap = drm_legacy_mmap, .poll = drm_poll, #ifdef CONFIG_COMPAT .compat_ioctl = r128_compat_ioctl, @@ -62,6 +62,7 @@ static struct drm_driver driver = { .load = r128_driver_load, .preclose = r128_driver_preclose, .lastclose = r128_driver_lastclose, + .set_busid = drm_pci_set_busid, .get_vblank_counter = r128_get_vblank_counter, .enable_vblank = r128_enable_vblank, .disable_vblank = r128_disable_vblank, diff --git a/drivers/gpu/drm/r128/r128_drv.h b/drivers/gpu/drm/r128/r128_drv.h index 5bf3f5ff805d941b74e05ab26f5119b8f154c057..723e5d6f10a4b96ed1a7db6f5f71f6c51d6a1f3c 100644 --- a/drivers/gpu/drm/r128/r128_drv.h +++ b/drivers/gpu/drm/r128/r128_drv.h @@ -35,6 +35,9 @@ #ifndef __R128_DRV_H__ #define __R128_DRV_H__ +#include +#include + /* General customization: */ #define DRIVER_AUTHOR "Gareth Hughes, VA Linux Systems Inc." diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index f77b7135ee4cde8e07b96c619f0bca8f94cdc37b..d01b87991422588c24b8f477a1050641cb683aa1 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -60,7 +60,7 @@ radeon-y := radeon_drv.o # add UMS driver radeon-$(CONFIG_DRM_RADEON_UMS)+= radeon_cp.o radeon_state.o radeon_mem.o \ - radeon_irq.o r300_cmdbuf.o r600_cp.o r600_blit.o + radeon_irq.o r300_cmdbuf.o r600_cp.o r600_blit.o drm_buffer.o # add KMS driver radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ @@ -72,7 +72,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ radeon_cs.o radeon_bios.o radeon_benchmark.o r100.o r300.o r420.o \ rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \ r200.o radeon_legacy_tv.o r600_cs.o r600_blit_shaders.o \ - radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o dce3_1_afmt.o \ + radeon_pm.o atombios_dp.o r600_hdmi.o dce3_1_afmt.o \ evergreen.o evergreen_cs.o evergreen_blit_shaders.o \ evergreen_hdmi.o radeon_trace_points.o ni.o cayman_blit_shaders.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.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 \ 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 \ - ci_dpm.o dce6_afmt.o radeon_vm.o radeon_ucode.o radeon_ib.o + ci_dpm.o dce6_afmt.o radeon_vm.o radeon_ucode.o radeon_ib.o radeon_mn.o # add async DMA block radeon-y += \ diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index ac14b67621d36f7a068de85103e06ba644915ea3..95d5d4ab3335edd2c86146c487c9cbd7bc269182 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -232,8 +232,8 @@ void radeon_dp_aux_init(struct radeon_connector *radeon_connector) /***** general DP utility functions *****/ -#define DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200 -#define DP_PRE_EMPHASIS_MAX DP_TRAIN_PRE_EMPHASIS_9_5 +#define DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_LEVEL_3 +#define DP_PRE_EMPHASIS_MAX DP_TRAIN_PRE_EMPH_LEVEL_3 static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE], int lane_count, diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index a7f2ddf09a9d20024a0cca95e70d3f3b8ff5fc12..b8cd7975f797e7d09bb3792ff3af15462cba68a6 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -291,29 +291,6 @@ static void radeon_atom_backlight_exit(struct radeon_encoder *encoder) bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, struct drm_display_mode *mode); - -static inline bool radeon_encoder_is_digital(struct drm_encoder *encoder) -{ - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_LVDS: - case ENCODER_OBJECT_ID_INTERNAL_TMDS1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: - case ENCODER_OBJECT_ID_INTERNAL_LVTM1: - case ENCODER_OBJECT_ID_INTERNAL_DVO1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: - case ENCODER_OBJECT_ID_INTERNAL_DDI: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: - return true; - default: - return false; - } -} - static bool radeon_atom_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c index f81d7ca134db19d12782f2005177639e64b6a44c..300d971187c4fca19f5b9676cb92d74c6b712969 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.c +++ b/drivers/gpu/drm/radeon/btc_dpm.c @@ -1170,23 +1170,6 @@ static const struct radeon_blacklist_clocks btc_blacklist_clocks[] = { 25000, 30000, RADEON_SCLK_UP } }; -void btc_get_max_clock_from_voltage_dependency_table(struct radeon_clock_voltage_dependency_table *table, - u32 *max_clock) -{ - u32 i, clock = 0; - - if ((table == NULL) || (table->count == 0)) { - *max_clock = clock; - return; - } - - for (i = 0; i < table->count; i++) { - if (clock < table->entries[i].clk) - clock = table->entries[i].clk; - } - *max_clock = clock; -} - void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table, u32 clock, u16 max_voltage, u16 *voltage) { @@ -2099,7 +2082,6 @@ static void btc_apply_state_adjust_rules(struct radeon_device *rdev, bool disable_mclk_switching; u32 mclk, sclk; u16 vddc, vddci; - u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc; if ((rdev->pm.dpm.new_active_crtc_count > 1) || btc_dpm_vblank_too_short(rdev)) @@ -2141,39 +2123,6 @@ static void btc_apply_state_adjust_rules(struct radeon_device *rdev, ps->low.vddci = max_limits->vddci; } - /* limit clocks to max supported clocks based on voltage dependency tables */ - btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk, - &max_sclk_vddc); - btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk, - &max_mclk_vddci); - btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk, - &max_mclk_vddc); - - if (max_sclk_vddc) { - if (ps->low.sclk > max_sclk_vddc) - ps->low.sclk = max_sclk_vddc; - if (ps->medium.sclk > max_sclk_vddc) - ps->medium.sclk = max_sclk_vddc; - if (ps->high.sclk > max_sclk_vddc) - ps->high.sclk = max_sclk_vddc; - } - if (max_mclk_vddci) { - if (ps->low.mclk > max_mclk_vddci) - ps->low.mclk = max_mclk_vddci; - if (ps->medium.mclk > max_mclk_vddci) - ps->medium.mclk = max_mclk_vddci; - if (ps->high.mclk > max_mclk_vddci) - ps->high.mclk = max_mclk_vddci; - } - if (max_mclk_vddc) { - if (ps->low.mclk > max_mclk_vddc) - ps->low.mclk = max_mclk_vddc; - if (ps->medium.mclk > max_mclk_vddc) - ps->medium.mclk = max_mclk_vddc; - if (ps->high.mclk > max_mclk_vddc) - ps->high.mclk = max_mclk_vddc; - } - /* XXX validate the min clocks required for display */ if (disable_mclk_switching) { diff --git a/drivers/gpu/drm/radeon/btc_dpm.h b/drivers/gpu/drm/radeon/btc_dpm.h index 3b6f12b7760ba48066f2144b73e0dc82c6521946..1a15e0e41950604ec8c27df9b11c6933c43d622d 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.h +++ b/drivers/gpu/drm/radeon/btc_dpm.h @@ -46,8 +46,6 @@ void btc_adjust_clock_combinations(struct radeon_device *rdev, struct rv7xx_pl *pl); void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table, u32 clock, u16 max_voltage, u16 *voltage); -void btc_get_max_clock_from_voltage_dependency_table(struct radeon_clock_voltage_dependency_table *table, - u32 *max_clock); void btc_apply_voltage_delta_rules(struct radeon_device *rdev, u16 max_vddc, u16 max_vddci, u16 *vddc, u16 *vddci); diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index d416bb2ff48dae0376b15e4789652799ff0638c3..f5c8c0445a94bd1b75a48c38be49a09301e8674b 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c @@ -162,8 +162,6 @@ static const struct ci_pt_config_reg didt_config_ci[] = }; extern u8 rv770_get_memory_module_index(struct radeon_device *rdev); -extern void btc_get_max_clock_from_voltage_dependency_table(struct radeon_clock_voltage_dependency_table *table, - u32 *max_clock); extern int ni_copy_and_switch_arb_sets(struct radeon_device *rdev, u32 arb_freq_src, u32 arb_freq_dest); extern u8 si_get_ddr3_mclk_frequency_ratio(u32 memory_clock); @@ -748,7 +746,6 @@ static void ci_apply_state_adjust_rules(struct radeon_device *rdev, struct radeon_clock_and_voltage_limits *max_limits; bool disable_mclk_switching; u32 sclk, mclk; - u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc; int i; if (rps->vce_active) { @@ -784,29 +781,6 @@ static void ci_apply_state_adjust_rules(struct radeon_device *rdev, } } - /* limit clocks to max supported clocks based on voltage dependency tables */ - btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk, - &max_sclk_vddc); - btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk, - &max_mclk_vddci); - btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk, - &max_mclk_vddc); - - for (i = 0; i < ps->performance_level_count; i++) { - if (max_sclk_vddc) { - if (ps->performance_levels[i].sclk > max_sclk_vddc) - ps->performance_levels[i].sclk = max_sclk_vddc; - } - if (max_mclk_vddci) { - if (ps->performance_levels[i].mclk > max_mclk_vddci) - ps->performance_levels[i].mclk = max_mclk_vddci; - } - if (max_mclk_vddc) { - if (ps->performance_levels[i].mclk > max_mclk_vddc) - ps->performance_levels[i].mclk = max_mclk_vddc; - } - } - /* XXX validate the min clocks required for display */ if (disable_mclk_switching) { @@ -5293,9 +5267,13 @@ int ci_dpm_init(struct radeon_device *rdev) void ci_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, struct seq_file *m) { + struct ci_power_info *pi = ci_get_pi(rdev); + struct radeon_ps *rps = &pi->current_rps; u32 sclk = ci_get_average_sclk_freq(rdev); u32 mclk = ci_get_average_mclk_freq(rdev); + seq_printf(m, "uvd %sabled\n", pi->uvd_enabled ? "en" : "dis"); + seq_printf(m, "vce %sabled\n", rps->vce_active ? "en" : "dis"); seq_printf(m, "power level avg sclk: %u mclk: %u\n", sclk, mclk); } diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 3d546c606b43b6fd366a1257c751b16b5aaa2df5..377afa504d2bd045cfdc5f2eed06599610c30920 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -3959,18 +3959,19 @@ bool cik_semaphore_ring_emit(struct radeon_device *rdev, * @src_offset: src GPU address * @dst_offset: dst GPU address * @num_gpu_pages: number of GPU pages to xfer - * @fence: radeon fence object + * @resv: reservation object to sync to * * Copy GPU paging using the CP DMA engine (CIK+). * Used by the radeon ttm implementation to move pages if * registered as the asic copy callback. */ -int cik_copy_cpdma(struct radeon_device *rdev, - uint64_t src_offset, uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence) +struct radeon_fence *cik_copy_cpdma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv) { struct radeon_semaphore *sem = NULL; + struct radeon_fence *fence; int ring_index = rdev->asic->copy.blit_ring_index; struct radeon_ring *ring = &rdev->ring[ring_index]; u32 size_in_bytes, cur_size_in_bytes, control; @@ -3980,7 +3981,7 @@ int cik_copy_cpdma(struct radeon_device *rdev, r = radeon_semaphore_create(rdev, &sem); if (r) { DRM_ERROR("radeon: moving bo (%d).\n", r); - return r; + return ERR_PTR(r); } size_in_bytes = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT); @@ -3989,10 +3990,10 @@ int cik_copy_cpdma(struct radeon_device *rdev, if (r) { DRM_ERROR("radeon: moving bo (%d).\n", r); radeon_semaphore_free(rdev, &sem, NULL); - return r; + return ERR_PTR(r); } - radeon_semaphore_sync_to(sem, *fence); + radeon_semaphore_sync_resv(rdev, sem, resv, false); radeon_semaphore_sync_rings(rdev, sem, ring->idx); for (i = 0; i < num_loops; i++) { @@ -4014,17 +4015,17 @@ int cik_copy_cpdma(struct radeon_device *rdev, dst_offset += cur_size_in_bytes; } - r = radeon_fence_emit(rdev, fence, ring->idx); + r = radeon_fence_emit(rdev, &fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); radeon_semaphore_free(rdev, &sem, NULL); - return r; + return ERR_PTR(r); } radeon_ring_unlock_commit(rdev, ring, false); - radeon_semaphore_free(rdev, &sem, *fence); + radeon_semaphore_free(rdev, &sem, fence); - return r; + return fence; } /* @@ -4234,7 +4235,7 @@ static int cik_cp_gfx_load_microcode(struct radeon_device *rdev) WREG32(CP_PFP_UCODE_ADDR, 0); for (i = 0; i < fw_size; i++) WREG32(CP_PFP_UCODE_DATA, le32_to_cpup(fw_data++)); - WREG32(CP_PFP_UCODE_ADDR, 0); + WREG32(CP_PFP_UCODE_ADDR, le32_to_cpu(pfp_hdr->header.ucode_version)); /* CE */ fw_data = (const __le32 *) @@ -4243,7 +4244,7 @@ static int cik_cp_gfx_load_microcode(struct radeon_device *rdev) WREG32(CP_CE_UCODE_ADDR, 0); for (i = 0; i < fw_size; i++) WREG32(CP_CE_UCODE_DATA, le32_to_cpup(fw_data++)); - WREG32(CP_CE_UCODE_ADDR, 0); + WREG32(CP_CE_UCODE_ADDR, le32_to_cpu(ce_hdr->header.ucode_version)); /* ME */ fw_data = (const __be32 *) @@ -4252,7 +4253,8 @@ static int cik_cp_gfx_load_microcode(struct radeon_device *rdev) WREG32(CP_ME_RAM_WADDR, 0); for (i = 0; i < fw_size; i++) WREG32(CP_ME_RAM_DATA, le32_to_cpup(fw_data++)); - WREG32(CP_ME_RAM_WADDR, 0); + WREG32(CP_ME_RAM_WADDR, le32_to_cpu(me_hdr->header.ucode_version)); + WREG32(CP_ME_RAM_RADDR, le32_to_cpu(me_hdr->header.ucode_version)); } else { const __be32 *fw_data; @@ -4278,10 +4280,6 @@ static int cik_cp_gfx_load_microcode(struct radeon_device *rdev) WREG32(CP_ME_RAM_WADDR, 0); } - WREG32(CP_PFP_UCODE_ADDR, 0); - WREG32(CP_CE_UCODE_ADDR, 0); - WREG32(CP_ME_RAM_WADDR, 0); - WREG32(CP_ME_RAM_RADDR, 0); return 0; } @@ -4563,7 +4561,7 @@ static int cik_cp_compute_load_microcode(struct radeon_device *rdev) WREG32(CP_MEC_ME1_UCODE_ADDR, 0); for (i = 0; i < fw_size; i++) WREG32(CP_MEC_ME1_UCODE_DATA, le32_to_cpup(fw_data++)); - WREG32(CP_MEC_ME1_UCODE_ADDR, 0); + WREG32(CP_MEC_ME1_UCODE_ADDR, le32_to_cpu(mec_hdr->header.ucode_version)); /* MEC2 */ if (rdev->family == CHIP_KAVERI) { @@ -4577,7 +4575,7 @@ static int cik_cp_compute_load_microcode(struct radeon_device *rdev) WREG32(CP_MEC_ME2_UCODE_ADDR, 0); for (i = 0; i < fw_size; i++) WREG32(CP_MEC_ME2_UCODE_DATA, le32_to_cpup(fw_data++)); - WREG32(CP_MEC_ME2_UCODE_ADDR, 0); + WREG32(CP_MEC_ME2_UCODE_ADDR, le32_to_cpu(mec2_hdr->header.ucode_version)); } } else { const __be32 *fw_data; @@ -4689,7 +4687,7 @@ static int cik_mec_init(struct radeon_device *rdev) r = radeon_bo_create(rdev, rdev->mec.num_mec *rdev->mec.num_pipe * MEC_HPD_SIZE * 2, PAGE_SIZE, true, - RADEON_GEM_DOMAIN_GTT, 0, NULL, + RADEON_GEM_DOMAIN_GTT, 0, NULL, NULL, &rdev->mec.hpd_eop_obj); if (r) { dev_warn(rdev->dev, "(%d) create HDP EOP bo failed\n", r); @@ -4860,7 +4858,7 @@ static int cik_cp_compute_resume(struct radeon_device *rdev) sizeof(struct bonaire_mqd), PAGE_SIZE, true, RADEON_GEM_DOMAIN_GTT, 0, NULL, - &rdev->ring[idx].mqd_obj); + NULL, &rdev->ring[idx].mqd_obj); if (r) { dev_warn(rdev->dev, "(%d) create MQD bo failed\n", r); return r; @@ -6226,7 +6224,7 @@ static int cik_rlc_resume(struct radeon_device *rdev) WREG32(RLC_GPM_UCODE_ADDR, 0); for (i = 0; i < size; i++) WREG32(RLC_GPM_UCODE_DATA, le32_to_cpup(fw_data++)); - WREG32(RLC_GPM_UCODE_ADDR, 0); + WREG32(RLC_GPM_UCODE_ADDR, le32_to_cpu(hdr->header.ucode_version)); } else { const __be32 *fw_data; @@ -8255,8 +8253,10 @@ int cik_irq_process(struct radeon_device *rdev) } if (queue_hotplug) schedule_work(&rdev->hotplug_work); - if (queue_reset) - schedule_work(&rdev->reset_work); + if (queue_reset) { + rdev->needs_reset = true; + wake_up_all(&rdev->fence_queue); + } if (queue_thermal) schedule_work(&rdev->pm.dpm.thermal.work); rdev->ih.rptr = rptr; diff --git a/drivers/gpu/drm/radeon/cik_sdma.c b/drivers/gpu/drm/radeon/cik_sdma.c index c4ffa54b1e3df503dc5dd9645765b2f0927e87b2..c77dad1a45769b151526377fac7e601129ee9221 100644 --- a/drivers/gpu/drm/radeon/cik_sdma.c +++ b/drivers/gpu/drm/radeon/cik_sdma.c @@ -530,18 +530,19 @@ void cik_sdma_fini(struct radeon_device *rdev) * @src_offset: src GPU address * @dst_offset: dst GPU address * @num_gpu_pages: number of GPU pages to xfer - * @fence: radeon fence object + * @resv: reservation object to sync to * * Copy GPU paging using the DMA engine (CIK). * Used by the radeon ttm implementation to move pages if * registered as the asic copy callback. */ -int cik_copy_dma(struct radeon_device *rdev, - uint64_t src_offset, uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence) +struct radeon_fence *cik_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv) { struct radeon_semaphore *sem = NULL; + struct radeon_fence *fence; int ring_index = rdev->asic->copy.dma_ring_index; struct radeon_ring *ring = &rdev->ring[ring_index]; u32 size_in_bytes, cur_size_in_bytes; @@ -551,7 +552,7 @@ int cik_copy_dma(struct radeon_device *rdev, r = radeon_semaphore_create(rdev, &sem); if (r) { DRM_ERROR("radeon: moving bo (%d).\n", r); - return r; + return ERR_PTR(r); } size_in_bytes = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT); @@ -560,10 +561,10 @@ int cik_copy_dma(struct radeon_device *rdev, if (r) { DRM_ERROR("radeon: moving bo (%d).\n", r); radeon_semaphore_free(rdev, &sem, NULL); - return r; + return ERR_PTR(r); } - radeon_semaphore_sync_to(sem, *fence); + radeon_semaphore_sync_resv(rdev, sem, resv, false); radeon_semaphore_sync_rings(rdev, sem, ring->idx); for (i = 0; i < num_loops; i++) { @@ -582,17 +583,17 @@ int cik_copy_dma(struct radeon_device *rdev, dst_offset += cur_size_in_bytes; } - r = radeon_fence_emit(rdev, fence, ring->idx); + r = radeon_fence_emit(rdev, &fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); radeon_semaphore_free(rdev, &sem, NULL); - return r; + return ERR_PTR(r); } radeon_ring_unlock_commit(rdev, ring, false); - radeon_semaphore_free(rdev, &sem, *fence); + radeon_semaphore_free(rdev, &sem, fence); - return r; + return fence; } /** diff --git a/drivers/gpu/drm/radeon/dce3_1_afmt.c b/drivers/gpu/drm/radeon/dce3_1_afmt.c index 51800e340a57d5c6e5294aaeaa35f1422b606d4e..950af153f30e7a53748a0cdb943b9457db468f26 100644 --- a/drivers/gpu/drm/radeon/dce3_1_afmt.c +++ b/drivers/gpu/drm/radeon/dce3_1_afmt.c @@ -165,7 +165,7 @@ void dce3_1_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *m /* disable audio prior to setting up hw */ dig->afmt->pin = r600_audio_get_pin(rdev); - r600_audio_enable(rdev, dig->afmt->pin, false); + r600_audio_enable(rdev, dig->afmt->pin, 0); r600_audio_set_dto(encoder, mode->clock); @@ -240,5 +240,5 @@ void dce3_1_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *m r600_hdmi_audio_workaround(encoder); /* enable audio after to setting up hw */ - r600_audio_enable(rdev, dig->afmt->pin, true); + r600_audio_enable(rdev, dig->afmt->pin, 0xf); } diff --git a/drivers/gpu/drm/radeon/dce6_afmt.c b/drivers/gpu/drm/radeon/dce6_afmt.c index ab29f953a767318c5e06de2115a9cfde017a9c82..c0bbf68dbc274f4d2055ea8f19850cdf3f6c747f 100644 --- a/drivers/gpu/drm/radeon/dce6_afmt.c +++ b/drivers/gpu/drm/radeon/dce6_afmt.c @@ -284,13 +284,13 @@ static int dce6_audio_chipset_supported(struct radeon_device *rdev) void dce6_audio_enable(struct radeon_device *rdev, struct r600_audio_pin *pin, - bool enable) + u8 enable_mask) { if (!pin) return; - WREG32_ENDPOINT(pin->offset, AZ_F0_CODEC_PIN_CONTROL_HOTPLUG_CONTROL, - enable ? AUDIO_ENABLED : 0); + WREG32_ENDPOINT(pin->offset, AZ_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL, + enable_mask ? AUDIO_ENABLED : 0); } static const u32 pin_offsets[7] = diff --git a/drivers/gpu/drm/drm_buffer.c b/drivers/gpu/drm/radeon/drm_buffer.c similarity index 96% rename from drivers/gpu/drm/drm_buffer.c rename to drivers/gpu/drm/radeon/drm_buffer.c index 86a4a4a60afcd79a052856275690ac088a4ff7c2..f4e0f3a3d7b1036c7a8f6a16779d14bbe8589e4a 100644 --- a/drivers/gpu/drm/drm_buffer.c +++ b/drivers/gpu/drm/radeon/drm_buffer.c @@ -33,7 +33,7 @@ */ #include -#include +#include "drm_buffer.h" /** * Allocate the drm buffer object. @@ -86,7 +86,6 @@ int drm_buffer_alloc(struct drm_buffer **buf, int size) kfree(*buf); return -ENOMEM; } -EXPORT_SYMBOL(drm_buffer_alloc); /** * Copy the user data to the begin of the buffer and reset the processing @@ -123,7 +122,6 @@ int drm_buffer_copy_from_user(struct drm_buffer *buf, buf->iterator = 0; return 0; } -EXPORT_SYMBOL(drm_buffer_copy_from_user); /** * Free the drm buffer object @@ -141,7 +139,6 @@ void drm_buffer_free(struct drm_buffer *buf) kfree(buf); } } -EXPORT_SYMBOL(drm_buffer_free); /** * Read an object from buffer that may be split to multiple parts. If object @@ -178,4 +175,3 @@ void *drm_buffer_read_object(struct drm_buffer *buf, drm_buffer_advance(buf, objsize); return obj; } -EXPORT_SYMBOL(drm_buffer_read_object); diff --git a/include/drm/drm_buffer.h b/drivers/gpu/drm/radeon/drm_buffer.h similarity index 100% rename from include/drm/drm_buffer.h rename to drivers/gpu/drm/radeon/drm_buffer.h diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index e50807c29f696a6b8eb23cf1e5dd9ab7f58412ca..a31f1ca40c6a7e81d06309e82bba86d3192a3562 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -22,7 +22,6 @@ * Authors: Alex Deucher */ #include -#include #include #include #include "radeon.h" @@ -4023,7 +4022,7 @@ int sumo_rlc_init(struct radeon_device *rdev) if (rdev->rlc.save_restore_obj == NULL) { r = radeon_bo_create(rdev, dws * 4, PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, 0, NULL, - &rdev->rlc.save_restore_obj); + NULL, &rdev->rlc.save_restore_obj); if (r) { dev_warn(rdev->dev, "(%d) create RLC sr bo failed\n", r); return r; @@ -4102,7 +4101,7 @@ int sumo_rlc_init(struct radeon_device *rdev) if (rdev->rlc.clear_state_obj == NULL) { r = radeon_bo_create(rdev, dws * 4, PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, 0, NULL, - &rdev->rlc.clear_state_obj); + NULL, &rdev->rlc.clear_state_obj); if (r) { dev_warn(rdev->dev, "(%d) create RLC c bo failed\n", r); sumo_rlc_fini(rdev); @@ -4179,7 +4178,7 @@ int sumo_rlc_init(struct radeon_device *rdev) r = radeon_bo_create(rdev, rdev->rlc.cp_table_size, PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, 0, NULL, - &rdev->rlc.cp_table_obj); + NULL, &rdev->rlc.cp_table_obj); if (r) { dev_warn(rdev->dev, "(%d) create RLC cp table bo failed\n", r); sumo_rlc_fini(rdev); diff --git a/drivers/gpu/drm/radeon/evergreen_dma.c b/drivers/gpu/drm/radeon/evergreen_dma.c index afaba388c36dec2fcd6cd5b986a9ab4d70406385..66bcfadeedd1a56265ea2f8121df864ecad4574e 100644 --- a/drivers/gpu/drm/radeon/evergreen_dma.c +++ b/drivers/gpu/drm/radeon/evergreen_dma.c @@ -104,12 +104,14 @@ void evergreen_dma_ring_ib_execute(struct radeon_device *rdev, * Used by the radeon ttm implementation to move pages if * registered as the asic copy callback. */ -int evergreen_copy_dma(struct radeon_device *rdev, - uint64_t src_offset, uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence) +struct radeon_fence *evergreen_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, + uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv) { struct radeon_semaphore *sem = NULL; + struct radeon_fence *fence; int ring_index = rdev->asic->copy.dma_ring_index; struct radeon_ring *ring = &rdev->ring[ring_index]; u32 size_in_dw, cur_size_in_dw; @@ -119,7 +121,7 @@ int evergreen_copy_dma(struct radeon_device *rdev, r = radeon_semaphore_create(rdev, &sem); if (r) { DRM_ERROR("radeon: moving bo (%d).\n", r); - return r; + return ERR_PTR(r); } size_in_dw = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT) / 4; @@ -128,10 +130,10 @@ int evergreen_copy_dma(struct radeon_device *rdev, if (r) { DRM_ERROR("radeon: moving bo (%d).\n", r); radeon_semaphore_free(rdev, &sem, NULL); - return r; + return ERR_PTR(r); } - radeon_semaphore_sync_to(sem, *fence); + radeon_semaphore_sync_resv(rdev, sem, resv, false); radeon_semaphore_sync_rings(rdev, sem, ring->idx); for (i = 0; i < num_loops; i++) { @@ -148,17 +150,17 @@ int evergreen_copy_dma(struct radeon_device *rdev, dst_offset += cur_size_in_dw * 4; } - r = radeon_fence_emit(rdev, fence, ring->idx); + r = radeon_fence_emit(rdev, &fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); radeon_semaphore_free(rdev, &sem, NULL); - return r; + return ERR_PTR(r); } radeon_ring_unlock_commit(rdev, ring, false); - radeon_semaphore_free(rdev, &sem, *fence); + radeon_semaphore_free(rdev, &sem, fence); - return r; + return fence; } /** diff --git a/drivers/gpu/drm/radeon/evergreen_hdmi.c b/drivers/gpu/drm/radeon/evergreen_hdmi.c index 278c7a139d748f770467a53d4cf19a920c9bb2ef..2514d659b1ba1783fb4c6474ea6e45d0b3a1b941 100644 --- a/drivers/gpu/drm/radeon/evergreen_hdmi.c +++ b/drivers/gpu/drm/radeon/evergreen_hdmi.c @@ -38,6 +38,37 @@ extern void dce6_afmt_select_pin(struct drm_encoder *encoder); extern void dce6_afmt_write_latency_fields(struct drm_encoder *encoder, struct drm_display_mode *mode); +/* enable the audio stream */ +static void dce4_audio_enable(struct radeon_device *rdev, + struct r600_audio_pin *pin, + u8 enable_mask) +{ + u32 tmp = RREG32(AZ_HOT_PLUG_CONTROL); + + if (!pin) + return; + + if (enable_mask) { + tmp |= AUDIO_ENABLED; + if (enable_mask & 1) + tmp |= PIN0_AUDIO_ENABLED; + if (enable_mask & 2) + tmp |= PIN1_AUDIO_ENABLED; + if (enable_mask & 4) + tmp |= PIN2_AUDIO_ENABLED; + if (enable_mask & 8) + tmp |= PIN3_AUDIO_ENABLED; + } else { + tmp &= ~(AUDIO_ENABLED | + PIN0_AUDIO_ENABLED | + PIN1_AUDIO_ENABLED | + PIN2_AUDIO_ENABLED | + PIN3_AUDIO_ENABLED); + } + + WREG32(AZ_HOT_PLUG_CONTROL, tmp); +} + /* * update the N and CTS parameters for a given pixel clock rate */ @@ -318,10 +349,10 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode /* disable audio prior to setting up hw */ if (ASIC_IS_DCE6(rdev)) { dig->afmt->pin = dce6_audio_get_pin(rdev); - dce6_audio_enable(rdev, dig->afmt->pin, false); + dce6_audio_enable(rdev, dig->afmt->pin, 0); } else { dig->afmt->pin = r600_audio_get_pin(rdev); - r600_audio_enable(rdev, dig->afmt->pin, false); + dce4_audio_enable(rdev, dig->afmt->pin, 0); } evergreen_audio_set_dto(encoder, mode->clock); @@ -463,13 +494,15 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode /* enable audio after to setting up hw */ if (ASIC_IS_DCE6(rdev)) - dce6_audio_enable(rdev, dig->afmt->pin, true); + dce6_audio_enable(rdev, dig->afmt->pin, 1); else - r600_audio_enable(rdev, dig->afmt->pin, true); + dce4_audio_enable(rdev, dig->afmt->pin, 0xf); } void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable) { + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; @@ -482,6 +515,14 @@ void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable) if (!enable && !dig->afmt->enabled) return; + if (!enable && dig->afmt->pin) { + if (ASIC_IS_DCE6(rdev)) + dce6_audio_enable(rdev, dig->afmt->pin, 0); + else + dce4_audio_enable(rdev, dig->afmt->pin, 0); + dig->afmt->pin = NULL; + } + dig->afmt->enabled = enable; DRM_DEBUG("%sabling HDMI interface @ 0x%04X for encoder 0x%x\n", diff --git a/drivers/gpu/drm/radeon/kv_dpm.c b/drivers/gpu/drm/radeon/kv_dpm.c index 67cb472d188ce05ec36c5a812ae5751fbb618254..1dd976f447faccd700fcfeac3ebc14cebbdd1320 100644 --- a/drivers/gpu/drm/radeon/kv_dpm.c +++ b/drivers/gpu/drm/radeon/kv_dpm.c @@ -2787,6 +2787,8 @@ void kv_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, tmp = (RREG32_SMC(SMU_VOLTAGE_STATUS) & SMU_VOLTAGE_CURRENT_LEVEL_MASK) >> SMU_VOLTAGE_CURRENT_LEVEL_SHIFT; vddc = kv_convert_8bit_index_to_voltage(rdev, (u16)tmp); + seq_printf(m, "uvd %sabled\n", pi->uvd_power_gated ? "dis" : "en"); + seq_printf(m, "vce %sabled\n", pi->vce_power_gated ? "dis" : "en"); seq_printf(m, "power level %d sclk: %u vddc: %u\n", current_index, sclk, vddc); } diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c index 01fc4888e6fea26b7f64fc9d02f73304f0b25a92..715b181c6243503228bb638f39c36a60ac4acf2a 100644 --- a/drivers/gpu/drm/radeon/ni_dpm.c +++ b/drivers/gpu/drm/radeon/ni_dpm.c @@ -789,7 +789,6 @@ static void ni_apply_state_adjust_rules(struct radeon_device *rdev, bool disable_mclk_switching; u32 mclk; u16 vddci; - u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc; int i; if ((rdev->pm.dpm.new_active_crtc_count > 1) || @@ -816,29 +815,6 @@ static void ni_apply_state_adjust_rules(struct radeon_device *rdev, } } - /* limit clocks to max supported clocks based on voltage dependency tables */ - btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk, - &max_sclk_vddc); - btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk, - &max_mclk_vddci); - btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk, - &max_mclk_vddc); - - for (i = 0; i < ps->performance_level_count; i++) { - if (max_sclk_vddc) { - if (ps->performance_levels[i].sclk > max_sclk_vddc) - ps->performance_levels[i].sclk = max_sclk_vddc; - } - if (max_mclk_vddci) { - if (ps->performance_levels[i].mclk > max_mclk_vddci) - ps->performance_levels[i].mclk = max_mclk_vddci; - } - if (max_mclk_vddc) { - if (ps->performance_levels[i].mclk > max_mclk_vddc) - ps->performance_levels[i].mclk = max_mclk_vddc; - } - } - /* XXX validate the min clocks required for display */ /* adjust low state */ diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index b0098e792e62337a6b4d511ca62baa7f37bc4eba..10f8be0ee1736394acaf9bac24eb516707917fd2 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -869,13 +869,14 @@ bool r100_semaphore_ring_emit(struct radeon_device *rdev, return false; } -int r100_copy_blit(struct radeon_device *rdev, - uint64_t src_offset, - uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence) +struct radeon_fence *r100_copy_blit(struct radeon_device *rdev, + uint64_t src_offset, + uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv) { struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + struct radeon_fence *fence; uint32_t cur_pages; uint32_t stride_bytes = RADEON_GPU_PAGE_SIZE; uint32_t pitch; @@ -896,7 +897,7 @@ int r100_copy_blit(struct radeon_device *rdev, r = radeon_ring_lock(rdev, ring, ndw); if (r) { DRM_ERROR("radeon: moving bo (%d) asking for %u dw.\n", r, ndw); - return -EINVAL; + return ERR_PTR(-EINVAL); } while (num_gpu_pages > 0) { cur_pages = num_gpu_pages; @@ -936,11 +937,13 @@ int r100_copy_blit(struct radeon_device *rdev, RADEON_WAIT_2D_IDLECLEAN | RADEON_WAIT_HOST_IDLECLEAN | RADEON_WAIT_DMA_GUI_IDLE); - if (fence) { - r = radeon_fence_emit(rdev, fence, RADEON_RING_TYPE_GFX_INDEX); + r = radeon_fence_emit(rdev, &fence, RADEON_RING_TYPE_GFX_INDEX); + if (r) { + radeon_ring_unlock_undo(rdev, ring); + return ERR_PTR(r); } radeon_ring_unlock_commit(rdev, ring, false); - return r; + return fence; } static int r100_cp_wait_for_idle(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/r200.c b/drivers/gpu/drm/radeon/r200.c index 67780374a65203fc57736510685f917b14d4ce89..732d4938aab75f91d1666960de2c0275de38d4a2 100644 --- a/drivers/gpu/drm/radeon/r200.c +++ b/drivers/gpu/drm/radeon/r200.c @@ -80,13 +80,14 @@ static int r200_get_vtx_size_0(uint32_t vtx_fmt_0) return vtx_size; } -int r200_copy_dma(struct radeon_device *rdev, - uint64_t src_offset, - uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence) +struct radeon_fence *r200_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, + uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv) { struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + struct radeon_fence *fence; uint32_t size; uint32_t cur_size; int i, num_loops; @@ -98,7 +99,7 @@ int r200_copy_dma(struct radeon_device *rdev, r = radeon_ring_lock(rdev, ring, num_loops * 4 + 64); if (r) { DRM_ERROR("radeon: moving bo (%d).\n", r); - return r; + return ERR_PTR(r); } /* Must wait for 2D idle & clean before DMA or hangs might happen */ radeon_ring_write(ring, PACKET0(RADEON_WAIT_UNTIL, 0)); @@ -118,11 +119,13 @@ int r200_copy_dma(struct radeon_device *rdev, } radeon_ring_write(ring, PACKET0(RADEON_WAIT_UNTIL, 0)); radeon_ring_write(ring, RADEON_WAIT_DMA_GUI_IDLE); - if (fence) { - r = radeon_fence_emit(rdev, fence, RADEON_RING_TYPE_GFX_INDEX); + r = radeon_fence_emit(rdev, &fence, RADEON_RING_TYPE_GFX_INDEX); + if (r) { + radeon_ring_unlock_undo(rdev, ring); + return ERR_PTR(r); } radeon_ring_unlock_commit(rdev, ring, false); - return r; + return fence; } diff --git a/drivers/gpu/drm/radeon/r300_cmdbuf.c b/drivers/gpu/drm/radeon/r300_cmdbuf.c index 84b1d5367a11f6950026337f76117bbb8edda0bb..9418e388b04544e39e016a54fcd33324590d0feb 100644 --- a/drivers/gpu/drm/radeon/r300_cmdbuf.c +++ b/drivers/gpu/drm/radeon/r300_cmdbuf.c @@ -34,10 +34,10 @@ */ #include -#include #include #include "radeon_drv.h" #include "r300_reg.h" +#include "drm_buffer.h" #include diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index ea5c9af722ef9f5d322d61a9d0ff628f8daa151f..56b02927cd3de5901da653bbea13cfcc7e9858ab 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -122,6 +122,94 @@ u32 r600_get_xclk(struct radeon_device *rdev) int r600_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) { + unsigned fb_div = 0, ref_div, vclk_div = 0, dclk_div = 0; + int r; + + /* bypass vclk and dclk with bclk */ + WREG32_P(CG_UPLL_FUNC_CNTL_2, + VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1), + ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK)); + + /* assert BYPASS_EN, deassert UPLL_RESET, UPLL_SLEEP and UPLL_CTLREQ */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~( + UPLL_RESET_MASK | UPLL_SLEEP_MASK | UPLL_CTLREQ_MASK)); + + if (rdev->family >= CHIP_RS780) + WREG32_P(GFX_MACRO_BYPASS_CNTL, UPLL_BYPASS_CNTL, + ~UPLL_BYPASS_CNTL); + + if (!vclk || !dclk) { + /* keep the Bypass mode, put PLL to sleep */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK); + return 0; + } + + if (rdev->clock.spll.reference_freq == 10000) + ref_div = 34; + else + ref_div = 4; + + r = radeon_uvd_calc_upll_dividers(rdev, vclk, dclk, 50000, 160000, + ref_div + 1, 0xFFF, 2, 30, ~0, + &fb_div, &vclk_div, &dclk_div); + if (r) + return r; + + if (rdev->family >= CHIP_RV670 && rdev->family < CHIP_RS780) + fb_div >>= 1; + else + fb_div |= 1; + + r = radeon_uvd_send_upll_ctlreq(rdev, CG_UPLL_FUNC_CNTL); + if (r) + return r; + + /* assert PLL_RESET */ + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_RESET_MASK, ~UPLL_RESET_MASK); + + /* For RS780 we have to choose ref clk */ + if (rdev->family >= CHIP_RS780) + WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_REFCLK_SRC_SEL_MASK, + ~UPLL_REFCLK_SRC_SEL_MASK); + + /* set the required fb, ref and post divder values */ + WREG32_P(CG_UPLL_FUNC_CNTL, + UPLL_FB_DIV(fb_div) | + UPLL_REF_DIV(ref_div), + ~(UPLL_FB_DIV_MASK | UPLL_REF_DIV_MASK)); + WREG32_P(CG_UPLL_FUNC_CNTL_2, + UPLL_SW_HILEN(vclk_div >> 1) | + UPLL_SW_LOLEN((vclk_div >> 1) + (vclk_div & 1)) | + UPLL_SW_HILEN2(dclk_div >> 1) | + UPLL_SW_LOLEN2((dclk_div >> 1) + (dclk_div & 1)) | + UPLL_DIVEN_MASK | UPLL_DIVEN2_MASK, + ~UPLL_SW_MASK); + + /* give the PLL some time to settle */ + mdelay(15); + + /* deassert PLL_RESET */ + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK); + + mdelay(15); + + /* deassert BYPASS EN */ + WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK); + + if (rdev->family >= CHIP_RS780) + WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~UPLL_BYPASS_CNTL); + + r = radeon_uvd_send_upll_ctlreq(rdev, CG_UPLL_FUNC_CNTL); + if (r) + return r; + + /* switch VCLK and DCLK selection */ + WREG32_P(CG_UPLL_FUNC_CNTL_2, + VCLK_SRC_SEL(2) | DCLK_SRC_SEL(2), + ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK)); + + mdelay(100); + return 0; } @@ -992,6 +1080,8 @@ static int r600_pcie_gart_enable(struct radeon_device *rdev) WREG32(MC_VM_L1_TLB_MCB_WR_GFX_CNTL, tmp); WREG32(MC_VM_L1_TLB_MCB_RD_PDMA_CNTL, tmp); WREG32(MC_VM_L1_TLB_MCB_WR_PDMA_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_UVD_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_WR_UVD_CNTL, tmp); WREG32(MC_VM_L1_TLB_MCB_RD_SEM_CNTL, tmp | ENABLE_SEMAPHORE_MODE); WREG32(MC_VM_L1_TLB_MCB_WR_SEM_CNTL, tmp | ENABLE_SEMAPHORE_MODE); WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, rdev->mc.gtt_start >> 12); @@ -1042,6 +1132,8 @@ static void r600_pcie_gart_disable(struct radeon_device *rdev) WREG32(MC_VM_L1_TLB_MCB_WR_SYS_CNTL, tmp); WREG32(MC_VM_L1_TLB_MCB_RD_HDP_CNTL, tmp); WREG32(MC_VM_L1_TLB_MCB_WR_HDP_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_RD_UVD_CNTL, tmp); + WREG32(MC_VM_L1_TLB_MCB_WR_UVD_CNTL, tmp); radeon_gart_table_vram_unpin(rdev); } @@ -1338,7 +1430,7 @@ int r600_vram_scratch_init(struct radeon_device *rdev) if (rdev->vram_scratch.robj == NULL) { r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE, PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, - 0, NULL, &rdev->vram_scratch.robj); + 0, NULL, NULL, &rdev->vram_scratch.robj); if (r) { return r; } @@ -2792,12 +2884,13 @@ bool r600_semaphore_ring_emit(struct radeon_device *rdev, * Used by the radeon ttm implementation to move pages if * registered as the asic copy callback. */ -int r600_copy_cpdma(struct radeon_device *rdev, - uint64_t src_offset, uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence) +struct radeon_fence *r600_copy_cpdma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv) { struct radeon_semaphore *sem = NULL; + struct radeon_fence *fence; int ring_index = rdev->asic->copy.blit_ring_index; struct radeon_ring *ring = &rdev->ring[ring_index]; u32 size_in_bytes, cur_size_in_bytes, tmp; @@ -2807,7 +2900,7 @@ int r600_copy_cpdma(struct radeon_device *rdev, r = radeon_semaphore_create(rdev, &sem); if (r) { DRM_ERROR("radeon: moving bo (%d).\n", r); - return r; + return ERR_PTR(r); } size_in_bytes = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT); @@ -2816,10 +2909,10 @@ int r600_copy_cpdma(struct radeon_device *rdev, if (r) { DRM_ERROR("radeon: moving bo (%d).\n", r); radeon_semaphore_free(rdev, &sem, NULL); - return r; + return ERR_PTR(r); } - radeon_semaphore_sync_to(sem, *fence); + radeon_semaphore_sync_resv(rdev, sem, resv, false); radeon_semaphore_sync_rings(rdev, sem, ring->idx); radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); @@ -2846,17 +2939,17 @@ int r600_copy_cpdma(struct radeon_device *rdev, radeon_ring_write(ring, (WAIT_UNTIL - PACKET3_SET_CONFIG_REG_OFFSET) >> 2); radeon_ring_write(ring, WAIT_CP_DMA_IDLE_bit); - r = radeon_fence_emit(rdev, fence, ring->idx); + r = radeon_fence_emit(rdev, &fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); radeon_semaphore_free(rdev, &sem, NULL); - return r; + return ERR_PTR(r); } radeon_ring_unlock_commit(rdev, ring, false); - radeon_semaphore_free(rdev, &sem, *fence); + radeon_semaphore_free(rdev, &sem, fence); - return r; + return fence; } int r600_set_surface_reg(struct radeon_device *rdev, int reg, @@ -2907,6 +3000,18 @@ static int r600_startup(struct radeon_device *rdev) return r; } + if (rdev->has_uvd) { + r = uvd_v1_0_resume(rdev); + if (!r) { + r = radeon_fence_driver_start_ring(rdev, R600_RING_TYPE_UVD_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing UVD fences (%d).\n", r); + } + } + if (r) + rdev->ring[R600_RING_TYPE_UVD_INDEX].ring_size = 0; + } + /* Enable IRQ */ if (!rdev->irq.installed) { r = radeon_irq_kms_init(rdev); @@ -2935,6 +3040,18 @@ static int r600_startup(struct radeon_device *rdev) if (r) return r; + if (rdev->has_uvd) { + ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX]; + if (ring->ring_size) { + r = radeon_ring_init(rdev, ring, ring->ring_size, 0, + RADEON_CP_PACKET2); + if (!r) + r = uvd_v1_0_init(rdev); + if (r) + DRM_ERROR("radeon: failed initializing UVD (%d).\n", r); + } + } + r = radeon_ib_pool_init(rdev); if (r) { dev_err(rdev->dev, "IB initialization failed (%d).\n", r); @@ -2994,6 +3111,10 @@ int r600_suspend(struct radeon_device *rdev) radeon_pm_suspend(rdev); r600_audio_fini(rdev); r600_cp_stop(rdev); + if (rdev->has_uvd) { + uvd_v1_0_fini(rdev); + radeon_uvd_suspend(rdev); + } r600_irq_suspend(rdev); radeon_wb_disable(rdev); r600_pcie_gart_disable(rdev); @@ -3073,6 +3194,14 @@ int r600_init(struct radeon_device *rdev) rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ring_obj = NULL; r600_ring_init(rdev, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX], 1024 * 1024); + if (rdev->has_uvd) { + r = radeon_uvd_init(rdev); + if (!r) { + rdev->ring[R600_RING_TYPE_UVD_INDEX].ring_obj = NULL; + r600_ring_init(rdev, &rdev->ring[R600_RING_TYPE_UVD_INDEX], 4096); + } + } + rdev->ih.ring_obj = NULL; r600_ih_ring_init(rdev, 64 * 1024); @@ -3102,6 +3231,10 @@ void r600_fini(struct radeon_device *rdev) r600_audio_fini(rdev); r600_cp_fini(rdev); r600_irq_fini(rdev); + if (rdev->has_uvd) { + uvd_v1_0_fini(rdev); + radeon_uvd_fini(rdev); + } radeon_wb_fini(rdev); radeon_ib_pool_fini(rdev); radeon_irq_kms_fini(rdev); @@ -3235,7 +3368,7 @@ int r600_ih_ring_alloc(struct radeon_device *rdev) r = radeon_bo_create(rdev, rdev->ih.ring_size, PAGE_SIZE, true, RADEON_GEM_DOMAIN_GTT, 0, - NULL, &rdev->ih.ring_obj); + NULL, NULL, &rdev->ih.ring_obj); if (r) { DRM_ERROR("radeon: failed to create ih ring buffer (%d).\n", r); return r; diff --git a/drivers/gpu/drm/radeon/r600_audio.c b/drivers/gpu/drm/radeon/r600_audio.c deleted file mode 100644 index bffac10c4296b3b4c28d9f7a52322fe51e9ae7c5..0000000000000000000000000000000000000000 --- a/drivers/gpu/drm/radeon/r600_audio.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2008 Advanced Micro Devices, Inc. - * Copyright 2008 Red Hat Inc. - * Copyright 2009 Christian König. - * - * 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. - * - * Authors: Christian König - */ -#include -#include "radeon.h" -#include "radeon_reg.h" -#include "radeon_asic.h" -#include "atom.h" - -/* - * check if enc_priv stores radeon_encoder_atom_dig - */ -static bool radeon_dig_encoder(struct drm_encoder *encoder) -{ - struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); - switch (radeon_encoder->encoder_id) { - case ENCODER_OBJECT_ID_INTERNAL_LVDS: - case ENCODER_OBJECT_ID_INTERNAL_TMDS1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: - case ENCODER_OBJECT_ID_INTERNAL_LVTM1: - case ENCODER_OBJECT_ID_INTERNAL_DVO1: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: - case ENCODER_OBJECT_ID_INTERNAL_DDI: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: - case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: - case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: - return true; - } - return false; -} - -/* - * check if the chipset is supported - */ -static int r600_audio_chipset_supported(struct radeon_device *rdev) -{ - return ASIC_IS_DCE2(rdev) && !ASIC_IS_NODCE(rdev); -} - -struct r600_audio_pin r600_audio_status(struct radeon_device *rdev) -{ - struct r600_audio_pin status; - uint32_t value; - - value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL); - - /* number of channels */ - status.channels = (value & 0x7) + 1; - - /* bits per sample */ - switch ((value & 0xF0) >> 4) { - case 0x0: - status.bits_per_sample = 8; - break; - case 0x1: - status.bits_per_sample = 16; - break; - case 0x2: - status.bits_per_sample = 20; - break; - case 0x3: - status.bits_per_sample = 24; - break; - case 0x4: - status.bits_per_sample = 32; - break; - default: - dev_err(rdev->dev, "Unknown bits per sample 0x%x, using 16\n", - (int)value); - status.bits_per_sample = 16; - } - - /* current sampling rate in HZ */ - if (value & 0x4000) - status.rate = 44100; - else - status.rate = 48000; - status.rate *= ((value >> 11) & 0x7) + 1; - status.rate /= ((value >> 8) & 0x7) + 1; - - value = RREG32(R600_AUDIO_STATUS_BITS); - - /* iec 60958 status bits */ - status.status_bits = value & 0xff; - - /* iec 60958 category code */ - status.category_code = (value >> 8) & 0xff; - - return status; -} - -/* - * update all hdmi interfaces with current audio parameters - */ -void r600_audio_update_hdmi(struct work_struct *work) -{ - struct radeon_device *rdev = container_of(work, struct radeon_device, - audio_work); - struct drm_device *dev = rdev->ddev; - struct r600_audio_pin audio_status = r600_audio_status(rdev); - struct drm_encoder *encoder; - bool changed = false; - - if (rdev->audio.pin[0].channels != audio_status.channels || - rdev->audio.pin[0].rate != audio_status.rate || - rdev->audio.pin[0].bits_per_sample != audio_status.bits_per_sample || - rdev->audio.pin[0].status_bits != audio_status.status_bits || - rdev->audio.pin[0].category_code != audio_status.category_code) { - rdev->audio.pin[0] = audio_status; - changed = true; - } - - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - if (!radeon_dig_encoder(encoder)) - continue; - if (changed || r600_hdmi_buffer_status_changed(encoder)) - r600_hdmi_update_audio_settings(encoder); - } -} - -/* enable the audio stream */ -void r600_audio_enable(struct radeon_device *rdev, - struct r600_audio_pin *pin, - bool enable) -{ - u32 value = 0; - - if (!pin) - return; - - if (ASIC_IS_DCE4(rdev)) { - if (enable) { - value |= 0x81000000; /* Required to enable audio */ - value |= 0x0e1000f0; /* fglrx sets that too */ - } - WREG32(EVERGREEN_AUDIO_ENABLE, value); - } else { - WREG32_P(R600_AUDIO_ENABLE, - enable ? 0x81000000 : 0x0, ~0x81000000); - } -} - -/* - * initialize the audio vars - */ -int r600_audio_init(struct radeon_device *rdev) -{ - if (!radeon_audio || !r600_audio_chipset_supported(rdev)) - return 0; - - rdev->audio.enabled = true; - - rdev->audio.num_pins = 1; - rdev->audio.pin[0].channels = -1; - rdev->audio.pin[0].rate = -1; - rdev->audio.pin[0].bits_per_sample = -1; - rdev->audio.pin[0].status_bits = 0; - rdev->audio.pin[0].category_code = 0; - rdev->audio.pin[0].id = 0; - /* disable audio. it will be set up later */ - r600_audio_enable(rdev, &rdev->audio.pin[0], false); - - return 0; -} - -/* - * release the audio timer - * TODO: How to do this correctly on SMP systems? - */ -void r600_audio_fini(struct radeon_device *rdev) -{ - if (!rdev->audio.enabled) - return; - - r600_audio_enable(rdev, &rdev->audio.pin[0], false); - - rdev->audio.enabled = false; -} - -struct r600_audio_pin *r600_audio_get_pin(struct radeon_device *rdev) -{ - /* only one pin on 6xx-NI */ - return &rdev->audio.pin[0]; -} diff --git a/drivers/gpu/drm/radeon/r600_cp.c b/drivers/gpu/drm/radeon/r600_cp.c index 8c9b7e26533c6c81635a9dafa20e7cecb164b167..09e3f39925faa832661e59333bed740981c10621 100644 --- a/drivers/gpu/drm/radeon/r600_cp.c +++ b/drivers/gpu/drm/radeon/r600_cp.c @@ -1949,15 +1949,15 @@ int r600_do_cleanup_cp(struct drm_device *dev) #if __OS_HAS_AGP if (dev_priv->flags & RADEON_IS_AGP) { if (dev_priv->cp_ring != NULL) { - drm_core_ioremapfree(dev_priv->cp_ring, dev); + drm_legacy_ioremapfree(dev_priv->cp_ring, dev); dev_priv->cp_ring = NULL; } if (dev_priv->ring_rptr != NULL) { - drm_core_ioremapfree(dev_priv->ring_rptr, dev); + drm_legacy_ioremapfree(dev_priv->ring_rptr, dev); dev_priv->ring_rptr = NULL; } if (dev->agp_buffer_map != NULL) { - drm_core_ioremapfree(dev->agp_buffer_map, dev); + drm_legacy_ioremapfree(dev->agp_buffer_map, dev); dev->agp_buffer_map = NULL; } } else @@ -1968,7 +1968,7 @@ int r600_do_cleanup_cp(struct drm_device *dev) r600_page_table_cleanup(dev, &dev_priv->gart_info); if (dev_priv->gart_info.gart_table_location == DRM_ATI_GART_FB) { - drm_core_ioremapfree(&dev_priv->gart_info.mapping, dev); + drm_legacy_ioremapfree(&dev_priv->gart_info.mapping, dev); dev_priv->gart_info.addr = NULL; } } @@ -2052,27 +2052,27 @@ int r600_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init, dev_priv->buffers_offset = init->buffers_offset; dev_priv->gart_textures_offset = init->gart_textures_offset; - master_priv->sarea = drm_getsarea(dev); + master_priv->sarea = drm_legacy_getsarea(dev); if (!master_priv->sarea) { DRM_ERROR("could not find sarea!\n"); r600_do_cleanup_cp(dev); return -EINVAL; } - dev_priv->cp_ring = drm_core_findmap(dev, init->ring_offset); + dev_priv->cp_ring = drm_legacy_findmap(dev, init->ring_offset); if (!dev_priv->cp_ring) { DRM_ERROR("could not find cp ring region!\n"); r600_do_cleanup_cp(dev); return -EINVAL; } - dev_priv->ring_rptr = drm_core_findmap(dev, init->ring_rptr_offset); + dev_priv->ring_rptr = drm_legacy_findmap(dev, init->ring_rptr_offset); if (!dev_priv->ring_rptr) { DRM_ERROR("could not find ring read pointer!\n"); r600_do_cleanup_cp(dev); return -EINVAL; } dev->agp_buffer_token = init->buffers_offset; - dev->agp_buffer_map = drm_core_findmap(dev, init->buffers_offset); + dev->agp_buffer_map = drm_legacy_findmap(dev, init->buffers_offset); if (!dev->agp_buffer_map) { DRM_ERROR("could not find dma buffer region!\n"); r600_do_cleanup_cp(dev); @@ -2081,7 +2081,7 @@ int r600_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init, if (init->gart_textures_offset) { dev_priv->gart_textures = - drm_core_findmap(dev, init->gart_textures_offset); + drm_legacy_findmap(dev, init->gart_textures_offset); if (!dev_priv->gart_textures) { DRM_ERROR("could not find GART texture region!\n"); r600_do_cleanup_cp(dev); @@ -2092,9 +2092,9 @@ int r600_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init, #if __OS_HAS_AGP /* XXX */ if (dev_priv->flags & RADEON_IS_AGP) { - drm_core_ioremap_wc(dev_priv->cp_ring, dev); - drm_core_ioremap_wc(dev_priv->ring_rptr, dev); - drm_core_ioremap_wc(dev->agp_buffer_map, dev); + drm_legacy_ioremap_wc(dev_priv->cp_ring, dev); + drm_legacy_ioremap_wc(dev_priv->ring_rptr, dev); + drm_legacy_ioremap_wc(dev->agp_buffer_map, dev); if (!dev_priv->cp_ring->handle || !dev_priv->ring_rptr->handle || !dev->agp_buffer_map->handle) { @@ -2235,7 +2235,7 @@ int r600_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init, dev_priv->gart_info.mapping.size = dev_priv->gart_info.table_size; - drm_core_ioremap_wc(&dev_priv->gart_info.mapping, dev); + drm_legacy_ioremap_wc(&dev_priv->gart_info.mapping, dev); if (!dev_priv->gart_info.mapping.handle) { DRM_ERROR("ioremap failed.\n"); r600_do_cleanup_cp(dev); diff --git a/drivers/gpu/drm/radeon/r600_dma.c b/drivers/gpu/drm/radeon/r600_dma.c index a908daa006d23980bd51fe50d93e94ea3464aac5..100189ec5fa85133b840851d4def58bbbae6ffc0 100644 --- a/drivers/gpu/drm/radeon/r600_dma.c +++ b/drivers/gpu/drm/radeon/r600_dma.c @@ -427,18 +427,19 @@ void r600_dma_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) * @src_offset: src GPU address * @dst_offset: dst GPU address * @num_gpu_pages: number of GPU pages to xfer - * @fence: radeon fence object + * @resv: reservation object to sync to * * Copy GPU paging using the DMA engine (r6xx). * Used by the radeon ttm implementation to move pages if * registered as the asic copy callback. */ -int r600_copy_dma(struct radeon_device *rdev, - uint64_t src_offset, uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence) +struct radeon_fence *r600_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv) { struct radeon_semaphore *sem = NULL; + struct radeon_fence *fence; int ring_index = rdev->asic->copy.dma_ring_index; struct radeon_ring *ring = &rdev->ring[ring_index]; u32 size_in_dw, cur_size_in_dw; @@ -448,7 +449,7 @@ int r600_copy_dma(struct radeon_device *rdev, r = radeon_semaphore_create(rdev, &sem); if (r) { DRM_ERROR("radeon: moving bo (%d).\n", r); - return r; + return ERR_PTR(r); } size_in_dw = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT) / 4; @@ -457,10 +458,10 @@ int r600_copy_dma(struct radeon_device *rdev, if (r) { DRM_ERROR("radeon: moving bo (%d).\n", r); radeon_semaphore_free(rdev, &sem, NULL); - return r; + return ERR_PTR(r); } - radeon_semaphore_sync_to(sem, *fence); + radeon_semaphore_sync_resv(rdev, sem, resv, false); radeon_semaphore_sync_rings(rdev, sem, ring->idx); for (i = 0; i < num_loops; i++) { @@ -477,15 +478,15 @@ int r600_copy_dma(struct radeon_device *rdev, dst_offset += cur_size_in_dw * 4; } - r = radeon_fence_emit(rdev, fence, ring->idx); + r = radeon_fence_emit(rdev, &fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); radeon_semaphore_free(rdev, &sem, NULL); - return r; + return ERR_PTR(r); } radeon_ring_unlock_commit(rdev, ring, false); - radeon_semaphore_free(rdev, &sem, *fence); + radeon_semaphore_free(rdev, &sem, fence); - return r; + return fence; } diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c index 26ef8ced6f89fd5388f80d2eb408c1a064af3b0e..b90dc0eb08e6637623af76e3f76911ced593fba3 100644 --- a/drivers/gpu/drm/radeon/r600_hdmi.c +++ b/drivers/gpu/drm/radeon/r600_hdmi.c @@ -71,6 +71,169 @@ static const struct radeon_hdmi_acr r600_hdmi_predefined_acr[] = { }; +/* + * check if the chipset is supported + */ +static int r600_audio_chipset_supported(struct radeon_device *rdev) +{ + return ASIC_IS_DCE2(rdev) && !ASIC_IS_NODCE(rdev); +} + +static struct r600_audio_pin r600_audio_status(struct radeon_device *rdev) +{ + struct r600_audio_pin status; + uint32_t value; + + value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL); + + /* number of channels */ + status.channels = (value & 0x7) + 1; + + /* bits per sample */ + switch ((value & 0xF0) >> 4) { + case 0x0: + status.bits_per_sample = 8; + break; + case 0x1: + status.bits_per_sample = 16; + break; + case 0x2: + status.bits_per_sample = 20; + break; + case 0x3: + status.bits_per_sample = 24; + break; + case 0x4: + status.bits_per_sample = 32; + break; + default: + dev_err(rdev->dev, "Unknown bits per sample 0x%x, using 16\n", + (int)value); + status.bits_per_sample = 16; + } + + /* current sampling rate in HZ */ + if (value & 0x4000) + status.rate = 44100; + else + status.rate = 48000; + status.rate *= ((value >> 11) & 0x7) + 1; + status.rate /= ((value >> 8) & 0x7) + 1; + + value = RREG32(R600_AUDIO_STATUS_BITS); + + /* iec 60958 status bits */ + status.status_bits = value & 0xff; + + /* iec 60958 category code */ + status.category_code = (value >> 8) & 0xff; + + return status; +} + +/* + * update all hdmi interfaces with current audio parameters + */ +void r600_audio_update_hdmi(struct work_struct *work) +{ + struct radeon_device *rdev = container_of(work, struct radeon_device, + audio_work); + struct drm_device *dev = rdev->ddev; + struct r600_audio_pin audio_status = r600_audio_status(rdev); + struct drm_encoder *encoder; + bool changed = false; + + if (rdev->audio.pin[0].channels != audio_status.channels || + rdev->audio.pin[0].rate != audio_status.rate || + rdev->audio.pin[0].bits_per_sample != audio_status.bits_per_sample || + rdev->audio.pin[0].status_bits != audio_status.status_bits || + rdev->audio.pin[0].category_code != audio_status.category_code) { + rdev->audio.pin[0] = audio_status; + changed = true; + } + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (!radeon_encoder_is_digital(encoder)) + continue; + if (changed || r600_hdmi_buffer_status_changed(encoder)) + r600_hdmi_update_audio_settings(encoder); + } +} + +/* enable the audio stream */ +void r600_audio_enable(struct radeon_device *rdev, + struct r600_audio_pin *pin, + u8 enable_mask) +{ + u32 tmp = RREG32(AZ_HOT_PLUG_CONTROL); + + if (!pin) + return; + + if (enable_mask) { + tmp |= AUDIO_ENABLED; + if (enable_mask & 1) + tmp |= PIN0_AUDIO_ENABLED; + if (enable_mask & 2) + tmp |= PIN1_AUDIO_ENABLED; + if (enable_mask & 4) + tmp |= PIN2_AUDIO_ENABLED; + if (enable_mask & 8) + tmp |= PIN3_AUDIO_ENABLED; + } else { + tmp &= ~(AUDIO_ENABLED | + PIN0_AUDIO_ENABLED | + PIN1_AUDIO_ENABLED | + PIN2_AUDIO_ENABLED | + PIN3_AUDIO_ENABLED); + } + + WREG32(AZ_HOT_PLUG_CONTROL, tmp); +} + +/* + * initialize the audio vars + */ +int r600_audio_init(struct radeon_device *rdev) +{ + if (!radeon_audio || !r600_audio_chipset_supported(rdev)) + return 0; + + rdev->audio.enabled = true; + + rdev->audio.num_pins = 1; + rdev->audio.pin[0].channels = -1; + rdev->audio.pin[0].rate = -1; + rdev->audio.pin[0].bits_per_sample = -1; + rdev->audio.pin[0].status_bits = 0; + rdev->audio.pin[0].category_code = 0; + rdev->audio.pin[0].id = 0; + /* disable audio. it will be set up later */ + r600_audio_enable(rdev, &rdev->audio.pin[0], 0); + + return 0; +} + +/* + * release the audio timer + * TODO: How to do this correctly on SMP systems? + */ +void r600_audio_fini(struct radeon_device *rdev) +{ + if (!rdev->audio.enabled) + return; + + r600_audio_enable(rdev, &rdev->audio.pin[0], 0); + + rdev->audio.enabled = false; +} + +struct r600_audio_pin *r600_audio_get_pin(struct radeon_device *rdev) +{ + /* only one pin on 6xx-NI */ + return &rdev->audio.pin[0]; +} + /* * calculate CTS and N values if they are not found in the table */ @@ -357,7 +520,7 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod /* disable audio prior to setting up hw */ dig->afmt->pin = r600_audio_get_pin(rdev); - r600_audio_enable(rdev, dig->afmt->pin, false); + r600_audio_enable(rdev, dig->afmt->pin, 0xf); r600_audio_set_dto(encoder, mode->clock); @@ -443,7 +606,7 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod WREG32(HDMI0_RAMP_CONTROL3 + offset, 0x00000001); /* enable audio after to setting up hw */ - r600_audio_enable(rdev, dig->afmt->pin, true); + r600_audio_enable(rdev, dig->afmt->pin, 0xf); } /** @@ -528,6 +691,11 @@ void r600_hdmi_enable(struct drm_encoder *encoder, bool enable) if (!enable && !dig->afmt->enabled) return; + if (!enable && dig->afmt->pin) { + r600_audio_enable(rdev, dig->afmt->pin, 0); + dig->afmt->pin = NULL; + } + /* Older chipsets require setting HDMI and routing manually */ if (!ASIC_IS_DCE3(rdev)) { if (enable) diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index 31e1052ad3e3780cec522ab8a2e04bb62698c573..1e8495cca41e9f5f611dfc3b84726104427969c7 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h @@ -323,11 +323,12 @@ #define HDP_TILING_CONFIG 0x2F3C #define HDP_DEBUG1 0x2F34 +#define MC_CONFIG 0x2000 #define MC_VM_AGP_TOP 0x2184 #define MC_VM_AGP_BOT 0x2188 #define MC_VM_AGP_BASE 0x218C #define MC_VM_FB_LOCATION 0x2180 -#define MC_VM_L1_TLB_MCD_RD_A_CNTL 0x219C +#define MC_VM_L1_TLB_MCB_RD_UVD_CNTL 0x2124 #define ENABLE_L1_TLB (1 << 0) #define ENABLE_L1_FRAGMENT_PROCESSING (1 << 1) #define ENABLE_L1_STRICT_ORDERING (1 << 2) @@ -347,12 +348,14 @@ #define EFFECTIVE_L1_QUEUE_SIZE(x) (((x) & 7) << 15) #define EFFECTIVE_L1_QUEUE_SIZE_MASK 0x00038000 #define EFFECTIVE_L1_QUEUE_SIZE_SHIFT 15 +#define MC_VM_L1_TLB_MCD_RD_A_CNTL 0x219C #define MC_VM_L1_TLB_MCD_RD_B_CNTL 0x21A0 #define MC_VM_L1_TLB_MCB_RD_GFX_CNTL 0x21FC #define MC_VM_L1_TLB_MCB_RD_HDP_CNTL 0x2204 #define MC_VM_L1_TLB_MCB_RD_PDMA_CNTL 0x2208 #define MC_VM_L1_TLB_MCB_RD_SEM_CNTL 0x220C #define MC_VM_L1_TLB_MCB_RD_SYS_CNTL 0x2200 +#define MC_VM_L1_TLB_MCB_WR_UVD_CNTL 0x212c #define MC_VM_L1_TLB_MCD_WR_A_CNTL 0x21A4 #define MC_VM_L1_TLB_MCD_WR_B_CNTL 0x21A8 #define MC_VM_L1_TLB_MCB_WR_GFX_CNTL 0x2210 @@ -366,6 +369,8 @@ #define MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2194 #define MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x2198 +#define RS_DQ_RD_RET_CONF 0x2348 + #define PA_CL_ENHANCE 0x8A14 #define CLIP_VTX_REORDER_ENA (1 << 0) #define NUM_CLIP_SEQ(x) ((x) << 1) @@ -922,6 +927,23 @@ # define TARGET_LINK_SPEED_MASK (0xf << 0) # define SELECTABLE_DEEMPHASIS (1 << 6) +/* Audio */ +#define AZ_HOT_PLUG_CONTROL 0x7300 +# define AZ_FORCE_CODEC_WAKE (1 << 0) +# define JACK_DETECTION_ENABLE (1 << 4) +# define UNSOLICITED_RESPONSE_ENABLE (1 << 8) +# define CODEC_HOT_PLUG_ENABLE (1 << 12) +# define AUDIO_ENABLED (1 << 31) +/* DCE3 adds */ +# define PIN0_JACK_DETECTION_ENABLE (1 << 4) +# define PIN1_JACK_DETECTION_ENABLE (1 << 5) +# define PIN2_JACK_DETECTION_ENABLE (1 << 6) +# define PIN3_JACK_DETECTION_ENABLE (1 << 7) +# define PIN0_AUDIO_ENABLED (1 << 24) +# define PIN1_AUDIO_ENABLED (1 << 25) +# define PIN2_AUDIO_ENABLED (1 << 26) +# define PIN3_AUDIO_ENABLED (1 << 27) + /* Audio clocks DCE 2.0/3.0 */ #define AUDIO_DTO 0x7340 # define AUDIO_DTO_PHASE(x) (((x) & 0xffff) << 0) @@ -1476,6 +1498,7 @@ #define UVD_CGC_GATE 0xf4a8 #define UVD_LMI_CTRL2 0xf4f4 #define UVD_MASTINT_EN 0xf500 +#define UVD_FW_START 0xf51C #define UVD_LMI_ADDR_EXT 0xf594 #define UVD_LMI_CTRL 0xf598 #define UVD_LMI_SWAP_CNTL 0xf5b4 @@ -1488,6 +1511,13 @@ #define UVD_MPC_SET_MUX 0xf5f4 #define UVD_MPC_SET_ALU 0xf5f8 +#define UVD_VCPU_CACHE_OFFSET0 0xf608 +#define UVD_VCPU_CACHE_SIZE0 0xf60c +#define UVD_VCPU_CACHE_OFFSET1 0xf610 +#define UVD_VCPU_CACHE_SIZE1 0xf614 +#define UVD_VCPU_CACHE_OFFSET2 0xf618 +#define UVD_VCPU_CACHE_SIZE2 0xf61c + #define UVD_VCPU_CNTL 0xf660 #define UVD_SOFT_RESET 0xf680 #define RBC_SOFT_RESET (1<<0) @@ -1517,9 +1547,35 @@ #define UVD_CONTEXT_ID 0xf6f4 +/* rs780 only */ +#define GFX_MACRO_BYPASS_CNTL 0x30c0 +#define SPLL_BYPASS_CNTL (1 << 0) +#define UPLL_BYPASS_CNTL (1 << 1) + +#define CG_UPLL_FUNC_CNTL 0x7e0 +# define UPLL_RESET_MASK 0x00000001 +# define UPLL_SLEEP_MASK 0x00000002 +# define UPLL_BYPASS_EN_MASK 0x00000004 # define UPLL_CTLREQ_MASK 0x00000008 +# define UPLL_FB_DIV(x) ((x) << 4) +# define UPLL_FB_DIV_MASK 0x0000FFF0 +# define UPLL_REF_DIV(x) ((x) << 16) +# define UPLL_REF_DIV_MASK 0x003F0000 +# define UPLL_REFCLK_SRC_SEL_MASK 0x20000000 # define UPLL_CTLACK_MASK 0x40000000 # define UPLL_CTLACK2_MASK 0x80000000 +#define CG_UPLL_FUNC_CNTL_2 0x7e4 +# define UPLL_SW_HILEN(x) ((x) << 0) +# define UPLL_SW_LOLEN(x) ((x) << 4) +# define UPLL_SW_HILEN2(x) ((x) << 8) +# define UPLL_SW_LOLEN2(x) ((x) << 12) +# define UPLL_DIVEN_MASK 0x00010000 +# define UPLL_DIVEN2_MASK 0x00020000 +# define UPLL_SW_MASK 0x0003FFFF +# define VCLK_SRC_SEL(x) ((x) << 20) +# define VCLK_SRC_SEL_MASK 0x01F00000 +# define DCLK_SRC_SEL(x) ((x) << 25) +# define DCLK_SRC_SEL_MASK 0x3E000000 /* * PM4 diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 3247bfd144106d54fa24161f898f24ae538f9a3c..f7c4b226a284a162fcd088210d85b7b3ff57a838 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -65,6 +65,8 @@ #include #include #include +#include +#include #include #include @@ -72,6 +74,8 @@ #include #include +#include + #include "radeon_family.h" #include "radeon_mode.h" #include "radeon_reg.h" @@ -120,9 +124,6 @@ extern int radeon_backlight; #define RADEONFB_CONN_LIMIT 4 #define RADEON_BIOS_NUM_SCRATCH 8 -/* fence seq are set to this number when signaled */ -#define RADEON_FENCE_SIGNALED_SEQ 0LL - /* internal ring indices */ /* r1xx+ has gfx CP ring */ #define RADEON_RING_TYPE_GFX_INDEX 0 @@ -350,28 +351,32 @@ extern void evergreen_tiling_fields(unsigned tiling_flags, unsigned *bankw, * Fences. */ struct radeon_fence_driver { + struct radeon_device *rdev; uint32_t scratch_reg; uint64_t gpu_addr; volatile uint32_t *cpu_addr; /* sync_seq is protected by ring emission lock */ uint64_t sync_seq[RADEON_NUM_RINGS]; atomic64_t last_seq; - bool initialized; + bool initialized, delayed_irq; + struct delayed_work lockup_work; }; struct radeon_fence { + struct fence base; + struct radeon_device *rdev; - struct kref kref; - /* protected by radeon_fence.lock */ uint64_t seq; /* RB, DMA, etc. */ unsigned ring; + + wait_queue_t fence_wake; }; int radeon_fence_driver_start_ring(struct radeon_device *rdev, int ring); int radeon_fence_driver_init(struct radeon_device *rdev); void radeon_fence_driver_fini(struct radeon_device *rdev); -void radeon_fence_driver_force_completion(struct radeon_device *rdev); +void radeon_fence_driver_force_completion(struct radeon_device *rdev, int ring); int radeon_fence_emit(struct radeon_device *rdev, struct radeon_fence **fence, int ring); void radeon_fence_process(struct radeon_device *rdev, int ring); bool radeon_fence_signaled(struct radeon_fence *fence); @@ -469,7 +474,7 @@ struct radeon_bo { struct list_head list; /* Protected by tbo.reserved */ u32 initial_domain; - u32 placements[3]; + struct ttm_place placements[3]; struct ttm_placement placement; struct ttm_buffer_object tbo; struct ttm_bo_kmap_obj kmap; @@ -489,6 +494,9 @@ struct radeon_bo { struct ttm_bo_kmap_obj dma_buf_vmap; pid_t pid; + + struct radeon_mn *mn; + struct interval_tree_node mn_it; }; #define gem_to_radeon_bo(gobj) container_of((gobj), struct radeon_bo, gem_base) @@ -580,8 +588,12 @@ bool radeon_semaphore_emit_signal(struct radeon_device *rdev, int ring, struct radeon_semaphore *semaphore); bool radeon_semaphore_emit_wait(struct radeon_device *rdev, int ring, struct radeon_semaphore *semaphore); -void radeon_semaphore_sync_to(struct radeon_semaphore *semaphore, - struct radeon_fence *fence); +void radeon_semaphore_sync_fence(struct radeon_semaphore *semaphore, + struct radeon_fence *fence); +int radeon_semaphore_sync_resv(struct radeon_device *rdev, + struct radeon_semaphore *semaphore, + struct reservation_object *resv, + bool shared); int radeon_semaphore_sync_rings(struct radeon_device *rdev, struct radeon_semaphore *semaphore, int waiting_ring); @@ -702,7 +714,7 @@ struct radeon_flip_work { uint64_t base; struct drm_pending_vblank_event *event; struct radeon_bo *old_rbo; - struct radeon_fence *fence; + struct fence *fence; }; struct r500_irq_stat_regs { @@ -780,6 +792,7 @@ struct radeon_irq { int radeon_irq_kms_init(struct radeon_device *rdev); void radeon_irq_kms_fini(struct radeon_device *rdev); void radeon_irq_kms_sw_irq_get(struct radeon_device *rdev, int ring); +bool radeon_irq_kms_sw_irq_get_delayed(struct radeon_device *rdev, int ring); void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev, int ring); void radeon_irq_kms_pflip_irq_get(struct radeon_device *rdev, int crtc); void radeon_irq_kms_pflip_irq_put(struct radeon_device *rdev, int crtc); @@ -1642,7 +1655,8 @@ int radeon_uvd_get_create_msg(struct radeon_device *rdev, int ring, uint32_t handle, struct radeon_fence **fence); int radeon_uvd_get_destroy_msg(struct radeon_device *rdev, int ring, uint32_t handle, struct radeon_fence **fence); -void radeon_uvd_force_into_uvd_segment(struct radeon_bo *rbo); +void radeon_uvd_force_into_uvd_segment(struct radeon_bo *rbo, + uint32_t allowed_domains); void radeon_uvd_free_handles(struct radeon_device *rdev, struct drm_file *filp); int radeon_uvd_cs_parse(struct radeon_cs_parser *parser); @@ -1731,6 +1745,11 @@ void radeon_test_ring_sync(struct radeon_device *rdev, struct radeon_ring *cpB); void radeon_test_syncing(struct radeon_device *rdev); +/* + * MMU Notifier + */ +int radeon_mn_register(struct radeon_bo *bo, unsigned long addr); +void radeon_mn_unregister(struct radeon_bo *bo); /* * Debugfs @@ -1845,24 +1864,24 @@ struct radeon_asic { } display; /* copy functions for bo handling */ struct { - int (*blit)(struct radeon_device *rdev, - uint64_t src_offset, - uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence); + struct radeon_fence *(*blit)(struct radeon_device *rdev, + uint64_t src_offset, + uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv); u32 blit_ring_index; - int (*dma)(struct radeon_device *rdev, - uint64_t src_offset, - uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence); + struct radeon_fence *(*dma)(struct radeon_device *rdev, + uint64_t src_offset, + uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv); u32 dma_ring_index; /* method used for bo copy */ - int (*copy)(struct radeon_device *rdev, - uint64_t src_offset, - uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence); + struct radeon_fence *(*copy)(struct radeon_device *rdev, + uint64_t src_offset, + uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv); /* ring used for bo copies */ u32 copy_ring_index; } copy; @@ -2144,6 +2163,8 @@ int radeon_gem_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); int radeon_gem_create_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); +int radeon_gem_userptr_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); int radeon_gem_pin_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int radeon_gem_unpin_ioctl(struct drm_device *dev, void *data, @@ -2300,6 +2321,7 @@ struct radeon_device { struct radeon_mman mman; struct radeon_fence_driver fence_drv[RADEON_NUM_RINGS]; wait_queue_head_t fence_queue; + unsigned fence_context; struct mutex ring_lock; struct radeon_ring ring[RADEON_NUM_RINGS]; bool ib_pool_ready; @@ -2318,7 +2340,7 @@ struct radeon_device { bool need_dma32; bool accel_working; bool fastfb_working; /* IGP feature*/ - bool needs_reset; + bool needs_reset, in_reset; struct radeon_surface_reg surface_regs[RADEON_GEM_MAX_SURFACES]; const struct firmware *me_fw; /* all family ME firmware */ const struct firmware *pfp_fw; /* r6/700 PFP firmware */ @@ -2339,7 +2361,6 @@ struct radeon_device { struct radeon_mec mec; struct work_struct hotplug_work; struct work_struct audio_work; - struct work_struct reset_work; int num_crtc; /* number of crtcs */ struct mutex dc_hw_i2c_mutex; /* display controller hw i2c mutex */ bool has_uvd; @@ -2376,6 +2397,9 @@ struct radeon_device { /* tracking pinned memory */ u64 vram_pin_size; u64 gart_pin_size; + + struct mutex mn_lock; + DECLARE_HASHTABLE(mn_hash, 7); }; bool radeon_is_px(struct drm_device *dev); @@ -2431,7 +2455,17 @@ void cik_mm_wdoorbell(struct radeon_device *rdev, u32 index, u32 v); /* * Cast helper */ -#define to_radeon_fence(p) ((struct radeon_fence *)(p)) +extern const struct fence_ops radeon_fence_ops; + +static inline struct radeon_fence *to_radeon_fence(struct fence *f) +{ + struct radeon_fence *__f = container_of(f, struct radeon_fence, base); + + if (__f->base.ops == &radeon_fence_ops) + return __f; + + return NULL; +} /* * Registers read & write functions. @@ -2751,18 +2785,25 @@ void radeon_atombios_fini(struct radeon_device *rdev); /* * RING helpers. */ -#if DRM_DEBUG_CODE == 0 + +/** + * radeon_ring_write - write a value to the ring + * + * @ring: radeon_ring structure holding ring information + * @v: dword (dw) value to write + * + * Write a value to the requested ring buffer (all asics). + */ static inline void radeon_ring_write(struct radeon_ring *ring, uint32_t v) { + if (ring->count_dw <= 0) + DRM_ERROR("radeon: writing more dwords to the ring than expected!\n"); + ring->ring[ring->wptr++] = v; ring->wptr &= ring->ptr_mask; ring->count_dw--; ring->ring_free_dw--; } -#else -/* With debugging this is just too big to inline */ -void radeon_ring_write(struct radeon_ring *ring, uint32_t v); -#endif /* * ASICs macro. @@ -2801,9 +2842,9 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v); #define radeon_hdmi_setmode(rdev, e, m) (rdev)->asic->display.hdmi_setmode((e), (m)) #define radeon_fence_ring_emit(rdev, r, fence) (rdev)->asic->ring[(r)]->emit_fence((rdev), (fence)) #define radeon_semaphore_ring_emit(rdev, r, cp, semaphore, emit_wait) (rdev)->asic->ring[(r)]->emit_semaphore((rdev), (cp), (semaphore), (emit_wait)) -#define radeon_copy_blit(rdev, s, d, np, f) (rdev)->asic->copy.blit((rdev), (s), (d), (np), (f)) -#define radeon_copy_dma(rdev, s, d, np, f) (rdev)->asic->copy.dma((rdev), (s), (d), (np), (f)) -#define radeon_copy(rdev, s, d, np, f) (rdev)->asic->copy.copy((rdev), (s), (d), (np), (f)) +#define radeon_copy_blit(rdev, s, d, np, resv) (rdev)->asic->copy.blit((rdev), (s), (d), (np), (resv)) +#define radeon_copy_dma(rdev, s, d, np, resv) (rdev)->asic->copy.dma((rdev), (s), (d), (np), (resv)) +#define radeon_copy(rdev, s, d, np, resv) (rdev)->asic->copy.copy((rdev), (s), (d), (np), (resv)) #define radeon_copy_blit_ring_index(rdev) (rdev)->asic->copy.blit_ring_index #define radeon_copy_dma_ring_index(rdev) (rdev)->asic->copy.dma_ring_index #define radeon_copy_ring_index(rdev) (rdev)->asic->copy.copy_ring_index @@ -2877,6 +2918,10 @@ extern void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enabl extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable); extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain); extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo); +extern int radeon_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr, + uint32_t flags); +extern bool radeon_ttm_tt_has_userptr(struct ttm_tt *ttm); +extern bool radeon_ttm_tt_is_readonly(struct ttm_tt *ttm); extern void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64 base); extern void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc); extern int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon); @@ -2934,10 +2979,10 @@ struct r600_audio_pin *r600_audio_get_pin(struct radeon_device *rdev); struct r600_audio_pin *dce6_audio_get_pin(struct radeon_device *rdev); void r600_audio_enable(struct radeon_device *rdev, struct r600_audio_pin *pin, - bool enable); + u8 enable_mask); void dce6_audio_enable(struct radeon_device *rdev, struct r600_audio_pin *pin, - bool enable); + u8 enable_mask); /* * R600 vram scratch functions diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 2dd5847f9b98e8a428e63b9ff9563f52b94d2709..850de57069bec0effcadd4a55c6b1abead242314 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -963,6 +963,19 @@ static struct radeon_asic r600_asic = { }, }; +static struct radeon_asic_ring rv6xx_uvd_ring = { + .ib_execute = &uvd_v1_0_ib_execute, + .emit_fence = &uvd_v1_0_fence_emit, + .emit_semaphore = &uvd_v1_0_semaphore_emit, + .cs_parse = &radeon_uvd_cs_parse, + .ring_test = &uvd_v1_0_ring_test, + .ib_test = &uvd_v1_0_ib_test, + .is_lockup = &radeon_ring_test_lockup, + .get_rptr = &uvd_v1_0_get_rptr, + .get_wptr = &uvd_v1_0_get_wptr, + .set_wptr = &uvd_v1_0_set_wptr, +}; + static struct radeon_asic rv6xx_asic = { .init = &r600_init, .fini = &r600_fini, @@ -982,6 +995,7 @@ static struct radeon_asic rv6xx_asic = { .ring = { [RADEON_RING_TYPE_GFX_INDEX] = &r600_gfx_ring, [R600_RING_TYPE_DMA_INDEX] = &r600_dma_ring, + [R600_RING_TYPE_UVD_INDEX] = &rv6xx_uvd_ring, }, .irq = { .set = &r600_irq_set, @@ -1072,6 +1086,7 @@ static struct radeon_asic rs780_asic = { .ring = { [RADEON_RING_TYPE_GFX_INDEX] = &r600_gfx_ring, [R600_RING_TYPE_DMA_INDEX] = &r600_dma_ring, + [R600_RING_TYPE_UVD_INDEX] = &rv6xx_uvd_ring, }, .irq = { .set = &r600_irq_set, @@ -2296,7 +2311,15 @@ int radeon_asic_init(struct radeon_device *rdev) case CHIP_RS780: case CHIP_RS880: rdev->asic = &rs780_asic; - rdev->has_uvd = true; + /* 760G/780V/880V don't have UVD */ + if ((rdev->pdev->device == 0x9616)|| + (rdev->pdev->device == 0x9611)|| + (rdev->pdev->device == 0x9613)|| + (rdev->pdev->device == 0x9711)|| + (rdev->pdev->device == 0x9713)) + rdev->has_uvd = false; + else + rdev->has_uvd = true; break; case CHIP_RV770: case CHIP_RV730: diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 7756bc1e1cd3ef088ac4bb96fa04a9ab632ad088..d8ace5b28a5b2e1455b5776129022300b3d1be3e 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -81,11 +81,11 @@ bool r100_semaphore_ring_emit(struct radeon_device *rdev, int r100_cs_parse(struct radeon_cs_parser *p); void r100_pll_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); uint32_t r100_pll_rreg(struct radeon_device *rdev, uint32_t reg); -int r100_copy_blit(struct radeon_device *rdev, - uint64_t src_offset, - uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence); +struct radeon_fence *r100_copy_blit(struct radeon_device *rdev, + uint64_t src_offset, + uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv); int r100_set_surface_reg(struct radeon_device *rdev, int reg, uint32_t tiling_flags, uint32_t pitch, uint32_t offset, uint32_t obj_size); @@ -152,11 +152,11 @@ void r100_gfx_set_wptr(struct radeon_device *rdev, /* * r200,rv250,rs300,rv280 */ -extern int r200_copy_dma(struct radeon_device *rdev, - uint64_t src_offset, - uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence); +struct radeon_fence *r200_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, + uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv); void r200_set_safe_registers(struct radeon_device *rdev); /* @@ -340,12 +340,14 @@ int r600_dma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring); void r600_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib); int r600_ring_test(struct radeon_device *rdev, struct radeon_ring *cp); int r600_dma_ring_test(struct radeon_device *rdev, struct radeon_ring *cp); -int r600_copy_cpdma(struct radeon_device *rdev, - uint64_t src_offset, uint64_t dst_offset, - unsigned num_gpu_pages, struct radeon_fence **fence); -int r600_copy_dma(struct radeon_device *rdev, - uint64_t src_offset, uint64_t dst_offset, - unsigned num_gpu_pages, struct radeon_fence **fence); +struct radeon_fence *r600_copy_cpdma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv); +struct radeon_fence *r600_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv); void r600_hpd_init(struct radeon_device *rdev); void r600_hpd_fini(struct radeon_device *rdev); bool r600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd); @@ -389,7 +391,6 @@ void r600_disable_interrupts(struct radeon_device *rdev); void r600_rlc_stop(struct radeon_device *rdev); /* r600 audio */ int r600_audio_init(struct radeon_device *rdev); -struct r600_audio_pin r600_audio_status(struct radeon_device *rdev); void r600_audio_fini(struct radeon_device *rdev); void r600_audio_set_dto(struct drm_encoder *encoder, u32 clock); void r600_hdmi_update_avi_infoframe(struct drm_encoder *encoder, void *buffer, @@ -461,10 +462,10 @@ bool rv770_page_flip_pending(struct radeon_device *rdev, int crtc); void r700_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc); void r700_cp_stop(struct radeon_device *rdev); void r700_cp_fini(struct radeon_device *rdev); -int rv770_copy_dma(struct radeon_device *rdev, - uint64_t src_offset, uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence); +struct radeon_fence *rv770_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv); u32 rv770_get_xclk(struct radeon_device *rdev); int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); int rv770_get_temp(struct radeon_device *rdev); @@ -535,10 +536,10 @@ void evergreen_dma_fence_ring_emit(struct radeon_device *rdev, struct radeon_fence *fence); void evergreen_dma_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib); -int evergreen_copy_dma(struct radeon_device *rdev, - uint64_t src_offset, uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence); +struct radeon_fence *evergreen_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv); void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable); void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode); int evergreen_get_temp(struct radeon_device *rdev); @@ -700,10 +701,10 @@ int si_vm_init(struct radeon_device *rdev); void si_vm_fini(struct radeon_device *rdev); void si_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm); int si_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib); -int si_copy_dma(struct radeon_device *rdev, - uint64_t src_offset, uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence); +struct radeon_fence *si_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv); void si_dma_vm_copy_pages(struct radeon_device *rdev, struct radeon_ib *ib, @@ -759,14 +760,14 @@ bool cik_sdma_semaphore_ring_emit(struct radeon_device *rdev, struct radeon_semaphore *semaphore, bool emit_wait); void cik_sdma_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib); -int cik_copy_dma(struct radeon_device *rdev, - uint64_t src_offset, uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence); -int cik_copy_cpdma(struct radeon_device *rdev, - uint64_t src_offset, uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence); +struct radeon_fence *cik_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv); +struct radeon_fence *cik_copy_cpdma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv); int cik_sdma_ring_test(struct radeon_device *rdev, struct radeon_ring *ring); int cik_sdma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring); bool cik_sdma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring); @@ -882,6 +883,7 @@ uint32_t uvd_v1_0_get_wptr(struct radeon_device *rdev, struct radeon_ring *ring); void uvd_v1_0_set_wptr(struct radeon_device *rdev, struct radeon_ring *ring); +int uvd_v1_0_resume(struct radeon_device *rdev); int uvd_v1_0_init(struct radeon_device *rdev); void uvd_v1_0_fini(struct radeon_device *rdev); @@ -889,6 +891,8 @@ int uvd_v1_0_start(struct radeon_device *rdev); void uvd_v1_0_stop(struct radeon_device *rdev); int uvd_v1_0_ring_test(struct radeon_device *rdev, struct radeon_ring *ring); +void uvd_v1_0_fence_emit(struct radeon_device *rdev, + struct radeon_fence *fence); int uvd_v1_0_ib_test(struct radeon_device *rdev, struct radeon_ring *ring); bool uvd_v1_0_semaphore_emit(struct radeon_device *rdev, struct radeon_ring *ring, diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index e74c7e387ddebfbb74d32866b39d2046d2632540..df69b92ba164158cc5e7ae79f7b59db65305dd49 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -458,7 +458,7 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev, return true; } -const int supported_devices_connector_convert[] = { +static const int supported_devices_connector_convert[] = { DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_VGA, DRM_MODE_CONNECTOR_DVII, @@ -477,7 +477,7 @@ const int supported_devices_connector_convert[] = { DRM_MODE_CONNECTOR_DisplayPort }; -const uint16_t supported_devices_connector_object_id_convert[] = { +static const uint16_t supported_devices_connector_object_id_convert[] = { CONNECTOR_OBJECT_ID_NONE, CONNECTOR_OBJECT_ID_VGA, CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I, /* not all boards support DL */ @@ -494,7 +494,7 @@ const uint16_t supported_devices_connector_object_id_convert[] = { CONNECTOR_OBJECT_ID_SVIDEO }; -const int object_connector_convert[] = { +static const int object_connector_convert[] = { DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_DVII, DRM_MODE_CONNECTOR_DVII, diff --git a/drivers/gpu/drm/radeon/radeon_benchmark.c b/drivers/gpu/drm/radeon/radeon_benchmark.c index 69f5695bdab9b42a3efad5332be67a9866b3336e..9e7f23dd14bd5992d73b72ddec32d084ee906aed 100644 --- a/drivers/gpu/drm/radeon/radeon_benchmark.c +++ b/drivers/gpu/drm/radeon/radeon_benchmark.c @@ -45,33 +45,29 @@ static int radeon_benchmark_do_move(struct radeon_device *rdev, unsigned size, for (i = 0; i < n; i++) { switch (flag) { case RADEON_BENCHMARK_COPY_DMA: - r = radeon_copy_dma(rdev, saddr, daddr, - size / RADEON_GPU_PAGE_SIZE, - &fence); + fence = radeon_copy_dma(rdev, saddr, daddr, + size / RADEON_GPU_PAGE_SIZE, + NULL); break; case RADEON_BENCHMARK_COPY_BLIT: - r = radeon_copy_blit(rdev, saddr, daddr, - size / RADEON_GPU_PAGE_SIZE, - &fence); + fence = radeon_copy_blit(rdev, saddr, daddr, + size / RADEON_GPU_PAGE_SIZE, + NULL); break; default: DRM_ERROR("Unknown copy method\n"); - r = -EINVAL; + return -EINVAL; } - if (r) - goto exit_do_move; + if (IS_ERR(fence)) + return PTR_ERR(fence); + r = radeon_fence_wait(fence, false); - if (r) - goto exit_do_move; radeon_fence_unref(&fence); + if (r) + return r; } end_jiffies = jiffies; - r = jiffies_to_msecs(end_jiffies - start_jiffies); - -exit_do_move: - if (fence) - radeon_fence_unref(&fence); - return r; + return jiffies_to_msecs(end_jiffies - start_jiffies); } @@ -97,7 +93,7 @@ static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size, int time; n = RADEON_BENCHMARK_ITERATIONS; - r = radeon_bo_create(rdev, size, PAGE_SIZE, true, sdomain, 0, NULL, &sobj); + r = radeon_bo_create(rdev, size, PAGE_SIZE, true, sdomain, 0, NULL, NULL, &sobj); if (r) { goto out_cleanup; } @@ -109,7 +105,7 @@ static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size, if (r) { goto out_cleanup; } - r = radeon_bo_create(rdev, size, PAGE_SIZE, true, ddomain, 0, NULL, &dobj); + r = radeon_bo_create(rdev, size, PAGE_SIZE, true, ddomain, 0, NULL, NULL, &dobj); if (r) { goto out_cleanup; } diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index 6651177110f08cd5afb9088f4849ad3f987f5a1b..3e5f6b71f3adad72b0aae490ede2ae1f426c9ff5 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -116,7 +116,7 @@ enum radeon_combios_connector { CONNECTOR_UNSUPPORTED_LEGACY }; -const int legacy_connector_convert[] = { +static const int legacy_connector_convert[] = { DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_DVID, DRM_MODE_CONNECTOR_VGA, diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c index bb0d5c3a8311bf0dc274c2b47930078d7e657852..ea134a7d51a51f8adb92856bb98a4e442a21e7f8 100644 --- a/drivers/gpu/drm/radeon/radeon_cp.c +++ b/drivers/gpu/drm/radeon/radeon_cp.c @@ -1298,27 +1298,27 @@ static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init, dev_priv->buffers_offset = init->buffers_offset; dev_priv->gart_textures_offset = init->gart_textures_offset; - master_priv->sarea = drm_getsarea(dev); + master_priv->sarea = drm_legacy_getsarea(dev); if (!master_priv->sarea) { DRM_ERROR("could not find sarea!\n"); radeon_do_cleanup_cp(dev); return -EINVAL; } - dev_priv->cp_ring = drm_core_findmap(dev, init->ring_offset); + dev_priv->cp_ring = drm_legacy_findmap(dev, init->ring_offset); if (!dev_priv->cp_ring) { DRM_ERROR("could not find cp ring region!\n"); radeon_do_cleanup_cp(dev); return -EINVAL; } - dev_priv->ring_rptr = drm_core_findmap(dev, init->ring_rptr_offset); + dev_priv->ring_rptr = drm_legacy_findmap(dev, init->ring_rptr_offset); if (!dev_priv->ring_rptr) { DRM_ERROR("could not find ring read pointer!\n"); radeon_do_cleanup_cp(dev); return -EINVAL; } dev->agp_buffer_token = init->buffers_offset; - dev->agp_buffer_map = drm_core_findmap(dev, init->buffers_offset); + dev->agp_buffer_map = drm_legacy_findmap(dev, init->buffers_offset); if (!dev->agp_buffer_map) { DRM_ERROR("could not find dma buffer region!\n"); radeon_do_cleanup_cp(dev); @@ -1327,7 +1327,7 @@ static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init, if (init->gart_textures_offset) { dev_priv->gart_textures = - drm_core_findmap(dev, init->gart_textures_offset); + drm_legacy_findmap(dev, init->gart_textures_offset); if (!dev_priv->gart_textures) { DRM_ERROR("could not find GART texture region!\n"); radeon_do_cleanup_cp(dev); @@ -1337,9 +1337,9 @@ static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init, #if __OS_HAS_AGP if (dev_priv->flags & RADEON_IS_AGP) { - drm_core_ioremap_wc(dev_priv->cp_ring, dev); - drm_core_ioremap_wc(dev_priv->ring_rptr, dev); - drm_core_ioremap_wc(dev->agp_buffer_map, dev); + drm_legacy_ioremap_wc(dev_priv->cp_ring, dev); + drm_legacy_ioremap_wc(dev_priv->ring_rptr, dev); + drm_legacy_ioremap_wc(dev->agp_buffer_map, dev); if (!dev_priv->cp_ring->handle || !dev_priv->ring_rptr->handle || !dev->agp_buffer_map->handle) { @@ -1475,7 +1475,7 @@ static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init, dev_priv->gart_info.mapping.size = dev_priv->gart_info.table_size; - drm_core_ioremap_wc(&dev_priv->gart_info.mapping, dev); + drm_legacy_ioremap_wc(&dev_priv->gart_info.mapping, dev); dev_priv->gart_info.addr = dev_priv->gart_info.mapping.handle; @@ -1569,15 +1569,15 @@ static int radeon_do_cleanup_cp(struct drm_device * dev) #if __OS_HAS_AGP if (dev_priv->flags & RADEON_IS_AGP) { if (dev_priv->cp_ring != NULL) { - drm_core_ioremapfree(dev_priv->cp_ring, dev); + drm_legacy_ioremapfree(dev_priv->cp_ring, dev); dev_priv->cp_ring = NULL; } if (dev_priv->ring_rptr != NULL) { - drm_core_ioremapfree(dev_priv->ring_rptr, dev); + drm_legacy_ioremapfree(dev_priv->ring_rptr, dev); dev_priv->ring_rptr = NULL; } if (dev->agp_buffer_map != NULL) { - drm_core_ioremapfree(dev->agp_buffer_map, dev); + drm_legacy_ioremapfree(dev->agp_buffer_map, dev); dev->agp_buffer_map = NULL; } } else @@ -1597,7 +1597,7 @@ static int radeon_do_cleanup_cp(struct drm_device * dev) if (dev_priv->gart_info.gart_table_location == DRM_ATI_GART_FB) { - drm_core_ioremapfree(&dev_priv->gart_info.mapping, dev); + drm_legacy_ioremapfree(&dev_priv->gart_info.mapping, dev); dev_priv->gart_info.addr = NULL; } } @@ -2106,9 +2106,9 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags) else dev_priv->flags |= RADEON_IS_PCI; - ret = drm_addmap(dev, pci_resource_start(dev->pdev, 2), - pci_resource_len(dev->pdev, 2), _DRM_REGISTERS, - _DRM_READ_ONLY | _DRM_DRIVER, &dev_priv->mmio); + ret = drm_legacy_addmap(dev, pci_resource_start(dev->pdev, 2), + pci_resource_len(dev->pdev, 2), _DRM_REGISTERS, + _DRM_READ_ONLY | _DRM_DRIVER, &dev_priv->mmio); if (ret != 0) return ret; @@ -2135,8 +2135,8 @@ int radeon_master_create(struct drm_device *dev, struct drm_master *master) /* prebuild the SAREA */ sareapage = max_t(unsigned long, SAREA_MAX, PAGE_SIZE); - ret = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK, - &master_priv->sarea); + ret = drm_legacy_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK, + &master_priv->sarea); if (ret) { DRM_ERROR("SAREA setup failed\n"); kfree(master_priv); @@ -2162,7 +2162,7 @@ void radeon_master_destroy(struct drm_device *dev, struct drm_master *master) master_priv->sarea_priv = NULL; if (master_priv->sarea) - drm_rmmap_locked(dev, master_priv->sarea); + drm_legacy_rmmap_locked(dev, master_priv->sarea); kfree(master_priv); @@ -2181,9 +2181,9 @@ int radeon_driver_firstopen(struct drm_device *dev) dev_priv->gart_info.table_size = RADEON_PCIGART_TABLE_SIZE; dev_priv->fb_aper_offset = pci_resource_start(dev->pdev, 0); - ret = drm_addmap(dev, dev_priv->fb_aper_offset, - pci_resource_len(dev->pdev, 0), _DRM_FRAME_BUFFER, - _DRM_WRITE_COMBINING, &map); + ret = drm_legacy_addmap(dev, dev_priv->fb_aper_offset, + pci_resource_len(dev->pdev, 0), + _DRM_FRAME_BUFFER, _DRM_WRITE_COMBINING, &map); if (ret != 0) return ret; @@ -2196,7 +2196,7 @@ int radeon_driver_unload(struct drm_device *dev) DRM_DEBUG("\n"); - drm_rmmap(dev, dev_priv->mmio); + drm_legacy_rmmap(dev, dev_priv->mmio); kfree(dev_priv); diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index 83f382e8e40e35cb380d4fc6fd835a7ddac9c01a..1c893447d7cd60fff05fa029dd9d3260d9495c11 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -78,7 +78,8 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p) struct radeon_cs_chunk *chunk; struct radeon_cs_buckets buckets; unsigned i, j; - bool duplicate; + bool duplicate, need_mmap_lock = false; + int r; if (p->chunk_relocs_idx == -1) { return 0; @@ -136,10 +137,13 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p) + !!r->write_domain; /* the first reloc of an UVD job is the msg and that must be in - VRAM, also but everything into VRAM on AGP cards to avoid - image corruptions */ + VRAM, also but everything into VRAM on AGP cards and older + IGP chips to avoid image corruptions */ if (p->ring == R600_RING_TYPE_UVD_INDEX && - (i == 0 || drm_pci_device_is_agp(p->rdev->ddev))) { + (i == 0 || drm_pci_device_is_agp(p->rdev->ddev) || + p->rdev->family == CHIP_RS780 || + p->rdev->family == CHIP_RS880)) { + /* TODO: is this still needed for NI+ ? */ p->relocs[i].prefered_domains = RADEON_GEM_DOMAIN_VRAM; @@ -165,7 +169,21 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p) p->relocs[i].allowed_domains = domain; } + if (radeon_ttm_tt_has_userptr(p->relocs[i].robj->tbo.ttm)) { + uint32_t domain = p->relocs[i].prefered_domains; + if (!(domain & RADEON_GEM_DOMAIN_GTT)) { + DRM_ERROR("Only RADEON_GEM_DOMAIN_GTT is " + "allowed for userptr BOs\n"); + return -EINVAL; + } + need_mmap_lock = true; + domain = RADEON_GEM_DOMAIN_GTT; + p->relocs[i].prefered_domains = domain; + p->relocs[i].allowed_domains = domain; + } + p->relocs[i].tv.bo = &p->relocs[i].robj->tbo; + p->relocs[i].tv.shared = !r->write_domain; p->relocs[i].handle = r->handle; radeon_cs_buckets_add(&buckets, &p->relocs[i].tv.head, @@ -177,8 +195,15 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p) if (p->cs_flags & RADEON_CS_USE_VM) p->vm_bos = radeon_vm_get_bos(p->rdev, p->ib.vm, &p->validated); + if (need_mmap_lock) + down_read(¤t->mm->mmap_sem); - return radeon_bo_list_validate(p->rdev, &p->ticket, &p->validated, p->ring); + r = radeon_bo_list_validate(p->rdev, &p->ticket, &p->validated, p->ring); + + if (need_mmap_lock) + up_read(¤t->mm->mmap_sem); + + return r; } static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority) @@ -224,17 +249,24 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority return 0; } -static void radeon_cs_sync_rings(struct radeon_cs_parser *p) +static int radeon_cs_sync_rings(struct radeon_cs_parser *p) { - int i; + int i, r = 0; for (i = 0; i < p->nrelocs; i++) { + struct reservation_object *resv; + if (!p->relocs[i].robj) continue; - radeon_semaphore_sync_to(p->ib.semaphore, - p->relocs[i].robj->tbo.sync_obj); + resv = p->relocs[i].robj->tbo.resv; + r = radeon_semaphore_sync_resv(p->rdev, p->ib.semaphore, resv, + p->relocs[i].tv.shared); + + if (r) + break; } + return r; } /* XXX: note that this is called from the legacy UMS CS ioctl as well */ @@ -403,7 +435,7 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error, bo ttm_eu_fence_buffer_objects(&parser->ticket, &parser->validated, - parser->ib.fence); + &parser->ib.fence->base); } else if (backoff) { ttm_eu_backoff_reservation(&parser->ticket, &parser->validated); @@ -444,13 +476,19 @@ static int radeon_cs_ib_chunk(struct radeon_device *rdev, return r; } + r = radeon_cs_sync_rings(parser); + if (r) { + if (r != -ERESTARTSYS) + DRM_ERROR("Failed to sync rings: %i\n", r); + return r; + } + if (parser->ring == R600_RING_TYPE_UVD_INDEX) radeon_uvd_note_usage(rdev); else if ((parser->ring == TN_RING_TYPE_VCE1_INDEX) || (parser->ring == TN_RING_TYPE_VCE2_INDEX)) radeon_vce_note_usage(rdev); - radeon_cs_sync_rings(parser); r = radeon_ib_schedule(rdev, &parser->ib, NULL, true); if (r) { DRM_ERROR("Failed to schedule IB !\n"); @@ -537,8 +575,14 @@ static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev, if (r) { goto out; } - radeon_cs_sync_rings(parser); - radeon_semaphore_sync_to(parser->ib.semaphore, vm->fence); + + r = radeon_cs_sync_rings(parser); + if (r) { + if (r != -ERESTARTSYS) + DRM_ERROR("Failed to sync rings: %i\n", r); + goto out; + } + radeon_semaphore_sync_fence(parser->ib.semaphore, vm->fence); if ((rdev->family >= CHIP_TAHITI) && (parser->chunk_const_ib_idx != -1)) { @@ -629,6 +673,13 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) up_read(&rdev->exclusive_lock); return -EBUSY; } + if (rdev->in_reset) { + up_read(&rdev->exclusive_lock); + r = radeon_gpu_reset(rdev); + if (!r) + r = -EAGAIN; + return r; + } /* initialize parser */ memset(&parser, 0, sizeof(struct radeon_cs_parser)); parser.filp = filp; diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 12c8329644c4af4e8fe5224fbbad5d888d580f28..f41cc1538e4851fcafcec22e29ffcfb07d7b0c98 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -434,7 +434,7 @@ int radeon_wb_init(struct radeon_device *rdev) if (rdev->wb.wb_obj == NULL) { r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE, PAGE_SIZE, true, - RADEON_GEM_DOMAIN_GTT, 0, NULL, + RADEON_GEM_DOMAIN_GTT, 0, NULL, NULL, &rdev->wb.wb_obj); if (r) { dev_warn(rdev->dev, "(%d) create WB bo failed\n", r); @@ -1257,6 +1257,7 @@ int radeon_device_init(struct radeon_device *rdev, for (i = 0; i < RADEON_NUM_RINGS; i++) { rdev->ring[i].idx = i; } + rdev->fence_context = fence_context_alloc(RADEON_NUM_RINGS); DRM_INFO("initializing kernel modesetting (%s 0x%04X:0x%04X 0x%04X:0x%04X).\n", radeon_family_name[rdev->family], pdev->vendor, pdev->device, @@ -1274,6 +1275,8 @@ int radeon_device_init(struct radeon_device *rdev, init_rwsem(&rdev->pm.mclk_lock); init_rwsem(&rdev->exclusive_lock); init_waitqueue_head(&rdev->irq.vblank_queue); + mutex_init(&rdev->mn_lock); + hash_init(rdev->mn_hash); r = radeon_gem_init(rdev); if (r) return r; @@ -1399,10 +1402,6 @@ int radeon_device_init(struct radeon_device *rdev, if (r) goto failed; - r = radeon_ib_ring_tests(rdev); - if (r) - DRM_ERROR("ib ring test failed (%d).\n", r); - r = radeon_gem_debugfs_init(rdev); if (r) { DRM_ERROR("registering gem debugfs failed (%d).\n", r); @@ -1420,6 +1419,10 @@ int radeon_device_init(struct radeon_device *rdev, goto failed; } + r = radeon_ib_ring_tests(rdev); + if (r) + DRM_ERROR("ib ring test failed (%d).\n", r); + if ((radeon_testing & 1)) { if (rdev->accel_working) radeon_test_moves(rdev); @@ -1497,7 +1500,6 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon) struct drm_crtc *crtc; struct drm_connector *connector; int i, r; - bool force_completion = false; if (dev == NULL || dev->dev_private == NULL) { return -ENODEV; @@ -1541,12 +1543,9 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon) r = radeon_fence_wait_empty(rdev, i); if (r) { /* delay GPU reset to resume */ - force_completion = true; + radeon_fence_driver_force_completion(rdev, i); } } - if (force_completion) { - radeon_fence_driver_force_completion(rdev); - } radeon_save_bios_scratch_regs(rdev); @@ -1686,8 +1685,6 @@ int radeon_gpu_reset(struct radeon_device *rdev) return 0; } - rdev->needs_reset = false; - radeon_save_bios_scratch_regs(rdev); /* block TTM */ resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev); @@ -1704,7 +1701,6 @@ int radeon_gpu_reset(struct radeon_device *rdev) } } -retry: r = radeon_asic_reset(rdev); if (!r) { dev_info(rdev->dev, "GPU reset succeeded, trying to resume\n"); @@ -1713,26 +1709,12 @@ int radeon_gpu_reset(struct radeon_device *rdev) radeon_restore_bios_scratch_regs(rdev); - if (!r) { - for (i = 0; i < RADEON_NUM_RINGS; ++i) { + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + if (!r && ring_data[i]) { radeon_ring_restore(rdev, &rdev->ring[i], ring_sizes[i], ring_data[i]); - ring_sizes[i] = 0; - ring_data[i] = NULL; - } - - r = radeon_ib_ring_tests(rdev); - if (r) { - dev_err(rdev->dev, "ib ring test failed (%d).\n", r); - if (saved) { - saved = false; - radeon_suspend(rdev); - goto retry; - } - } - } else { - radeon_fence_driver_force_completion(rdev); - for (i = 0; i < RADEON_NUM_RINGS; ++i) { + } else { + radeon_fence_driver_force_completion(rdev, i); kfree(ring_data[i]); } } @@ -1764,19 +1746,32 @@ int radeon_gpu_reset(struct radeon_device *rdev) /* reset hpd state */ radeon_hpd_init(rdev); + ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched); + + rdev->in_reset = true; + rdev->needs_reset = false; + + downgrade_write(&rdev->exclusive_lock); + drm_helper_resume_force_mode(rdev->ddev); /* set the power state here in case we are a PX system or headless */ if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) radeon_pm_compute_clocks(rdev); - ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched); - if (r) { + if (!r) { + r = radeon_ib_ring_tests(rdev); + if (r && saved) + r = -EAGAIN; + } else { /* bad news, how to tell it to userspace ? */ dev_info(rdev->dev, "GPU reset failed\n"); } - up_write(&rdev->exclusive_lock); + rdev->needs_reset = r == -EAGAIN; + rdev->in_reset = false; + + up_read(&rdev->exclusive_lock); return r; } diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 3fdf87318069f1a6b26e1e37f9fd504641b19132..00ead8c2758a972debddf97234e206d211db5d7a 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -402,12 +402,21 @@ static void radeon_flip_work_func(struct work_struct *__work) down_read(&rdev->exclusive_lock); if (work->fence) { - r = radeon_fence_wait(work->fence, false); - if (r == -EDEADLK) { - up_read(&rdev->exclusive_lock); - r = radeon_gpu_reset(rdev); - down_read(&rdev->exclusive_lock); - } + struct radeon_fence *fence; + + fence = to_radeon_fence(work->fence); + if (fence && fence->rdev == rdev) { + r = radeon_fence_wait(fence, false); + if (r == -EDEADLK) { + up_read(&rdev->exclusive_lock); + do { + r = radeon_gpu_reset(rdev); + } while (r == -EAGAIN); + down_read(&rdev->exclusive_lock); + } + } else + r = fence_wait(work->fence, false); + if (r) DRM_ERROR("failed to wait on page flip fence (%d)!\n", r); @@ -416,7 +425,8 @@ static void radeon_flip_work_func(struct work_struct *__work) * confused about which BO the CRTC is scanning out */ - radeon_fence_unref(&work->fence); + fence_put(work->fence); + work->fence = NULL; } /* We borrow the event spin lock for protecting flip_status */ @@ -474,11 +484,6 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc, obj = new_radeon_fb->obj; new_rbo = gem_to_radeon_bo(obj); - spin_lock(&new_rbo->tbo.bdev->fence_lock); - if (new_rbo->tbo.sync_obj) - work->fence = radeon_fence_ref(new_rbo->tbo.sync_obj); - spin_unlock(&new_rbo->tbo.bdev->fence_lock); - /* pin the new buffer */ DRM_DEBUG_DRIVER("flip-ioctl() cur_rbo = %p, new_rbo = %p\n", work->old_rbo, new_rbo); @@ -497,6 +502,7 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc, DRM_ERROR("failed to pin new rbo buffer before flip\n"); goto cleanup; } + work->fence = fence_get(reservation_object_get_excl(new_rbo->tbo.resv)); radeon_bo_get_tiling_flags(new_rbo, &tiling_flags, NULL); radeon_bo_unreserve(new_rbo); @@ -578,9 +584,8 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc, cleanup: drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base); - radeon_fence_unref(&work->fence); + fence_put(work->fence); kfree(work); - return r; } @@ -1917,7 +1922,7 @@ int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int fl /* In vblank? */ if (in_vbl) - ret |= DRM_SCANOUTPOS_INVBL; + ret |= DRM_SCANOUTPOS_IN_VBLANK; /* Is vpos outside nominal vblank area, but less than * 1/100 of a frame height away from start of vblank? diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index f9d17b29b343443de2f986b6008a9991085ff518..dcffa30ee2db3d2b990abc4089967c204984be5d 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -38,6 +38,8 @@ #include #include #include +#include + #include "drm_crtc_helper.h" /* * KMS wrapper. @@ -114,6 +116,9 @@ int radeon_gem_object_open(struct drm_gem_object *obj, struct drm_file *file_priv); void radeon_gem_object_close(struct drm_gem_object *obj, struct drm_file *file_priv); +struct dma_buf *radeon_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *gobj, + int flags); extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int flags, int *vpos, int *hpos, ktime_t *stime, @@ -130,7 +135,7 @@ int radeon_mode_dumb_create(struct drm_file *file_priv, struct drm_mode_create_dumb *args); struct sg_table *radeon_gem_prime_get_sg_table(struct drm_gem_object *obj); struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev, - size_t size, + struct dma_buf_attachment *, struct sg_table *sg); int radeon_gem_prime_pin(struct drm_gem_object *obj); void radeon_gem_prime_unpin(struct drm_gem_object *obj); @@ -309,7 +314,7 @@ static const struct file_operations radeon_driver_old_fops = { .open = drm_open, .release = drm_release, .unlocked_ioctl = drm_ioctl, - .mmap = drm_mmap, + .mmap = drm_legacy_mmap, .poll = drm_poll, .read = drm_read, #ifdef CONFIG_COMPAT @@ -329,6 +334,7 @@ static struct drm_driver driver_old = { .preclose = radeon_driver_preclose, .postclose = radeon_driver_postclose, .lastclose = radeon_driver_lastclose, + .set_busid = drm_pci_set_busid, .unload = radeon_driver_unload, .suspend = radeon_suspend, .resume = radeon_resume, @@ -553,6 +559,7 @@ static struct drm_driver kms_driver = { .preclose = radeon_driver_preclose_kms, .postclose = radeon_driver_postclose_kms, .lastclose = radeon_driver_lastclose_kms, + .set_busid = drm_pci_set_busid, .unload = radeon_driver_unload_kms, .get_vblank_counter = radeon_get_vblank_counter_kms, .enable_vblank = radeon_enable_vblank_kms, @@ -578,7 +585,7 @@ static struct drm_driver kms_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - .gem_prime_export = drm_gem_prime_export, + .gem_prime_export = radeon_gem_prime_export, .gem_prime_import = drm_gem_prime_import, .gem_prime_pin = radeon_gem_prime_pin, .gem_prime_unpin = radeon_gem_prime_unpin, diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h index dafd812e45710b0ffed36c33f3e37de20ec64733..46bd3938282ca84a6a19338f476daee049575ce7 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.h +++ b/drivers/gpu/drm/radeon/radeon_drv.h @@ -33,7 +33,9 @@ #include #include +#include +#include #include "radeon_family.h" /* General customization: diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c index 15edf23b465c2ba90cca2f2ffb7c242971bda2bf..9a19e52cc655bf8b3c697a7116e52e81b8060096 100644 --- a/drivers/gpu/drm/radeon/radeon_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_encoders.c @@ -410,3 +410,24 @@ bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder, } } +bool radeon_encoder_is_digital(struct drm_encoder *encoder) +{ + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + switch (radeon_encoder->encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_LVDS: + case ENCODER_OBJECT_ID_INTERNAL_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + case ENCODER_OBJECT_ID_INTERNAL_DVO1: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: + case ENCODER_OBJECT_ID_INTERNAL_DDI: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: + return true; + default: + return false; + } +} diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 94b0f2aa3d7c870b41bdf28be5d7bca5fecf4ff2..0ea1db83d57390e70874f3182893b277b56c6f94 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -189,7 +189,8 @@ static int radeonfb_create_pinned_object(struct radeon_fbdev *rfbdev, static int radeonfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { - struct radeon_fbdev *rfbdev = (struct radeon_fbdev *)helper; + struct radeon_fbdev *rfbdev = + container_of(helper, struct radeon_fbdev, helper); struct radeon_device *rdev = rfbdev->rdev; struct fb_info *info; struct drm_framebuffer *fb = NULL; diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index 913787085dfadf56cfd21ed5b771c072c78cd072..995167025282a13ff7fdbbe7a1555f0d228a751b 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -97,6 +97,25 @@ static u32 radeon_fence_read(struct radeon_device *rdev, int ring) return seq; } +/** + * radeon_fence_schedule_check - schedule lockup check + * + * @rdev: radeon_device pointer + * @ring: ring index we should work with + * + * Queues a delayed work item to check for lockups. + */ +static void radeon_fence_schedule_check(struct radeon_device *rdev, int ring) +{ + /* + * Do not reset the timer here with mod_delayed_work, + * this can livelock in an interaction with TTM delayed destroy. + */ + queue_delayed_work(system_power_efficient_wq, + &rdev->fence_drv[ring].lockup_work, + RADEON_FENCE_JIFFIES_TIMEOUT); +} + /** * radeon_fence_emit - emit a fence on the requested ring * @@ -111,30 +130,70 @@ int radeon_fence_emit(struct radeon_device *rdev, struct radeon_fence **fence, int ring) { + u64 seq = ++rdev->fence_drv[ring].sync_seq[ring]; + /* we are protected by the ring emission mutex */ *fence = kmalloc(sizeof(struct radeon_fence), GFP_KERNEL); if ((*fence) == NULL) { return -ENOMEM; } - kref_init(&((*fence)->kref)); (*fence)->rdev = rdev; - (*fence)->seq = ++rdev->fence_drv[ring].sync_seq[ring]; + (*fence)->seq = seq; (*fence)->ring = ring; + fence_init(&(*fence)->base, &radeon_fence_ops, + &rdev->fence_queue.lock, rdev->fence_context + ring, seq); radeon_fence_ring_emit(rdev, ring, *fence); trace_radeon_fence_emit(rdev->ddev, ring, (*fence)->seq); + radeon_fence_schedule_check(rdev, ring); return 0; } /** - * radeon_fence_process - process a fence + * radeon_fence_check_signaled - callback from fence_queue + * + * this function is called with fence_queue lock held, which is also used + * for the fence locking itself, so unlocked variants are used for + * fence_signal, and remove_wait_queue. + */ +static int radeon_fence_check_signaled(wait_queue_t *wait, unsigned mode, int flags, void *key) +{ + struct radeon_fence *fence; + u64 seq; + + fence = container_of(wait, struct radeon_fence, fence_wake); + + /* + * We cannot use radeon_fence_process here because we're already + * in the waitqueue, in a call from wake_up_all. + */ + seq = atomic64_read(&fence->rdev->fence_drv[fence->ring].last_seq); + if (seq >= fence->seq) { + int ret = fence_signal_locked(&fence->base); + + if (!ret) + FENCE_TRACE(&fence->base, "signaled from irq context\n"); + else + FENCE_TRACE(&fence->base, "was already signaled\n"); + + radeon_irq_kms_sw_irq_put(fence->rdev, fence->ring); + __remove_wait_queue(&fence->rdev->fence_queue, &fence->fence_wake); + fence_put(&fence->base); + } else + FENCE_TRACE(&fence->base, "pending\n"); + return 0; +} + +/** + * radeon_fence_activity - check for fence activity * * @rdev: radeon_device pointer * @ring: ring index the fence is associated with * - * Checks the current fence value and wakes the fence queue - * if the sequence number has increased (all asics). + * Checks the current fence value and calculates the last + * signalled fence value. Returns true if activity occured + * on the ring, and the fence_queue should be waken up. */ -void radeon_fence_process(struct radeon_device *rdev, int ring) +static bool radeon_fence_activity(struct radeon_device *rdev, int ring) { uint64_t seq, last_seq, last_emitted; unsigned count_loop = 0; @@ -190,23 +249,77 @@ void radeon_fence_process(struct radeon_device *rdev, int ring) } } while (atomic64_xchg(&rdev->fence_drv[ring].last_seq, seq) > seq); - if (wake) - wake_up_all(&rdev->fence_queue); + if (seq < last_emitted) + radeon_fence_schedule_check(rdev, ring); + + return wake; } /** - * radeon_fence_destroy - destroy a fence + * radeon_fence_check_lockup - check for hardware lockup * - * @kref: fence kref + * @work: delayed work item * - * Frees the fence object (all asics). + * Checks for fence activity and if there is none probe + * the hardware if a lockup occured. */ -static void radeon_fence_destroy(struct kref *kref) +static void radeon_fence_check_lockup(struct work_struct *work) { - struct radeon_fence *fence; + struct radeon_fence_driver *fence_drv; + struct radeon_device *rdev; + int ring; + + fence_drv = container_of(work, struct radeon_fence_driver, + lockup_work.work); + rdev = fence_drv->rdev; + ring = fence_drv - &rdev->fence_drv[0]; + + if (!down_read_trylock(&rdev->exclusive_lock)) { + /* just reschedule the check if a reset is going on */ + radeon_fence_schedule_check(rdev, ring); + return; + } + + if (fence_drv->delayed_irq && rdev->ddev->irq_enabled) { + unsigned long irqflags; + + fence_drv->delayed_irq = false; + spin_lock_irqsave(&rdev->irq.lock, irqflags); + radeon_irq_set(rdev); + spin_unlock_irqrestore(&rdev->irq.lock, irqflags); + } + + if (radeon_fence_activity(rdev, ring)) + wake_up_all(&rdev->fence_queue); - fence = container_of(kref, struct radeon_fence, kref); - kfree(fence); + else if (radeon_ring_is_lockup(rdev, ring, &rdev->ring[ring])) { + + /* good news we believe it's a lockup */ + dev_warn(rdev->dev, "GPU lockup (current fence id " + "0x%016llx last fence id 0x%016llx on ring %d)\n", + (uint64_t)atomic64_read(&fence_drv->last_seq), + fence_drv->sync_seq[ring], ring); + + /* remember that we need an reset */ + rdev->needs_reset = true; + wake_up_all(&rdev->fence_queue); + } + up_read(&rdev->exclusive_lock); +} + +/** + * radeon_fence_process - process a fence + * + * @rdev: radeon_device pointer + * @ring: ring index the fence is associated with + * + * Checks the current fence value and wakes the fence queue + * if the sequence number has increased (all asics). + */ +void radeon_fence_process(struct radeon_device *rdev, int ring) +{ + if (radeon_fence_activity(rdev, ring)) + wake_up_all(&rdev->fence_queue); } /** @@ -237,6 +350,75 @@ static bool radeon_fence_seq_signaled(struct radeon_device *rdev, return false; } +static bool radeon_fence_is_signaled(struct fence *f) +{ + struct radeon_fence *fence = to_radeon_fence(f); + struct radeon_device *rdev = fence->rdev; + unsigned ring = fence->ring; + u64 seq = fence->seq; + + if (atomic64_read(&rdev->fence_drv[ring].last_seq) >= seq) { + return true; + } + + if (down_read_trylock(&rdev->exclusive_lock)) { + radeon_fence_process(rdev, ring); + up_read(&rdev->exclusive_lock); + + if (atomic64_read(&rdev->fence_drv[ring].last_seq) >= seq) { + return true; + } + } + return false; +} + +/** + * radeon_fence_enable_signaling - enable signalling on fence + * @fence: fence + * + * This function is called with fence_queue lock held, and adds a callback + * to fence_queue that checks if this fence is signaled, and if so it + * signals the fence and removes itself. + */ +static bool radeon_fence_enable_signaling(struct fence *f) +{ + struct radeon_fence *fence = to_radeon_fence(f); + struct radeon_device *rdev = fence->rdev; + + if (atomic64_read(&rdev->fence_drv[fence->ring].last_seq) >= fence->seq) + return false; + + if (down_read_trylock(&rdev->exclusive_lock)) { + radeon_irq_kms_sw_irq_get(rdev, fence->ring); + + if (radeon_fence_activity(rdev, fence->ring)) + wake_up_all_locked(&rdev->fence_queue); + + /* did fence get signaled after we enabled the sw irq? */ + if (atomic64_read(&rdev->fence_drv[fence->ring].last_seq) >= fence->seq) { + radeon_irq_kms_sw_irq_put(rdev, fence->ring); + up_read(&rdev->exclusive_lock); + return false; + } + + up_read(&rdev->exclusive_lock); + } else { + /* we're probably in a lockup, lets not fiddle too much */ + if (radeon_irq_kms_sw_irq_get_delayed(rdev, fence->ring)) + rdev->fence_drv[fence->ring].delayed_irq = true; + radeon_fence_schedule_check(rdev, fence->ring); + } + + fence->fence_wake.flags = 0; + fence->fence_wake.private = NULL; + fence->fence_wake.func = radeon_fence_check_signaled; + __add_wait_queue(&rdev->fence_queue, &fence->fence_wake); + fence_get(f); + + FENCE_TRACE(&fence->base, "armed on ring %i!\n", fence->ring); + return true; +} + /** * radeon_fence_signaled - check if a fence has signaled * @@ -247,14 +429,15 @@ static bool radeon_fence_seq_signaled(struct radeon_device *rdev, */ bool radeon_fence_signaled(struct radeon_fence *fence) { - if (!fence) { + if (!fence) return true; - } - if (fence->seq == RADEON_FENCE_SIGNALED_SEQ) { - return true; - } + if (radeon_fence_seq_signaled(fence->rdev, fence->seq, fence->ring)) { - fence->seq = RADEON_FENCE_SIGNALED_SEQ; + int ret; + + ret = fence_signal(&fence->base); + if (!ret) + FENCE_TRACE(&fence->base, "signaled from radeon_fence_signaled\n"); return true; } return false; @@ -283,110 +466,70 @@ static bool radeon_fence_any_seq_signaled(struct radeon_device *rdev, u64 *seq) } /** - * radeon_fence_wait_seq - wait for a specific sequence numbers + * radeon_fence_wait_seq_timeout - wait for a specific sequence numbers * * @rdev: radeon device pointer * @target_seq: sequence number(s) we want to wait for * @intr: use interruptable sleep + * @timeout: maximum time to wait, or MAX_SCHEDULE_TIMEOUT for infinite wait * * Wait for the requested sequence number(s) to be written by any ring * (all asics). Sequnce number array is indexed by ring id. * @intr selects whether to use interruptable (true) or non-interruptable * (false) sleep when waiting for the sequence number. Helper function * for radeon_fence_wait_*(). - * Returns 0 if the sequence number has passed, error for all other cases. + * Returns remaining time if the sequence number has passed, 0 when + * the wait timeout, or an error for all other cases. * -EDEADLK is returned when a GPU lockup has been detected. */ -static int radeon_fence_wait_seq(struct radeon_device *rdev, u64 *target_seq, - bool intr) +static long radeon_fence_wait_seq_timeout(struct radeon_device *rdev, + u64 *target_seq, bool intr, + long timeout) { - uint64_t last_seq[RADEON_NUM_RINGS]; - bool signaled; - int i, r; - - while (!radeon_fence_any_seq_signaled(rdev, target_seq)) { + long r; + int i; - /* Save current sequence values, used to check for GPU lockups */ - for (i = 0; i < RADEON_NUM_RINGS; ++i) { - if (!target_seq[i]) - continue; + if (radeon_fence_any_seq_signaled(rdev, target_seq)) + return timeout; - last_seq[i] = atomic64_read(&rdev->fence_drv[i].last_seq); - trace_radeon_fence_wait_begin(rdev->ddev, i, target_seq[i]); - radeon_irq_kms_sw_irq_get(rdev, i); - } + /* enable IRQs and tracing */ + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + if (!target_seq[i]) + continue; - if (intr) { - r = wait_event_interruptible_timeout(rdev->fence_queue, ( - (signaled = radeon_fence_any_seq_signaled(rdev, target_seq)) - || rdev->needs_reset), RADEON_FENCE_JIFFIES_TIMEOUT); - } else { - r = wait_event_timeout(rdev->fence_queue, ( - (signaled = radeon_fence_any_seq_signaled(rdev, target_seq)) - || rdev->needs_reset), RADEON_FENCE_JIFFIES_TIMEOUT); - } + trace_radeon_fence_wait_begin(rdev->ddev, i, target_seq[i]); + radeon_irq_kms_sw_irq_get(rdev, i); + } - for (i = 0; i < RADEON_NUM_RINGS; ++i) { - if (!target_seq[i]) - continue; + if (intr) { + r = wait_event_interruptible_timeout(rdev->fence_queue, ( + radeon_fence_any_seq_signaled(rdev, target_seq) + || rdev->needs_reset), timeout); + } else { + r = wait_event_timeout(rdev->fence_queue, ( + radeon_fence_any_seq_signaled(rdev, target_seq) + || rdev->needs_reset), timeout); + } - radeon_irq_kms_sw_irq_put(rdev, i); - trace_radeon_fence_wait_end(rdev->ddev, i, target_seq[i]); - } + if (rdev->needs_reset) + r = -EDEADLK; - if (unlikely(r < 0)) - return r; + for (i = 0; i < RADEON_NUM_RINGS; ++i) { + if (!target_seq[i]) + continue; - if (unlikely(!signaled)) { - if (rdev->needs_reset) - return -EDEADLK; - - /* we were interrupted for some reason and fence - * isn't signaled yet, resume waiting */ - if (r) - continue; - - for (i = 0; i < RADEON_NUM_RINGS; ++i) { - if (!target_seq[i]) - continue; - - if (last_seq[i] != atomic64_read(&rdev->fence_drv[i].last_seq)) - break; - } - - if (i != RADEON_NUM_RINGS) - continue; - - for (i = 0; i < RADEON_NUM_RINGS; ++i) { - if (!target_seq[i]) - continue; - - if (radeon_ring_is_lockup(rdev, i, &rdev->ring[i])) - break; - } - - if (i < RADEON_NUM_RINGS) { - /* good news we believe it's a lockup */ - dev_warn(rdev->dev, "GPU lockup (waiting for " - "0x%016llx last fence id 0x%016llx on" - " ring %d)\n", - target_seq[i], last_seq[i], i); - - /* remember that we need an reset */ - rdev->needs_reset = true; - wake_up_all(&rdev->fence_queue); - return -EDEADLK; - } - } + radeon_irq_kms_sw_irq_put(rdev, i); + trace_radeon_fence_wait_end(rdev->ddev, i, target_seq[i]); } - return 0; + + return r; } /** * radeon_fence_wait - wait for a fence to signal * * @fence: radeon fence object - * @intr: use interruptable sleep + * @intr: use interruptible sleep * * Wait for the requested fence to signal (all asics). * @intr selects whether to use interruptable (true) or non-interruptable @@ -396,22 +539,26 @@ static int radeon_fence_wait_seq(struct radeon_device *rdev, u64 *target_seq, int radeon_fence_wait(struct radeon_fence *fence, bool intr) { uint64_t seq[RADEON_NUM_RINGS] = {}; - int r; + long r; - if (fence == NULL) { - WARN(1, "Querying an invalid fence : %p !\n", fence); - return -EINVAL; - } + /* + * This function should not be called on !radeon fences. + * If this is the case, it would mean this function can + * also be called on radeon fences belonging to another card. + * exclusive_lock is not held in that case. + */ + if (WARN_ON_ONCE(!to_radeon_fence(&fence->base))) + return fence_wait(&fence->base, intr); seq[fence->ring] = fence->seq; - if (seq[fence->ring] == RADEON_FENCE_SIGNALED_SEQ) - return 0; - - r = radeon_fence_wait_seq(fence->rdev, seq, intr); - if (r) + r = radeon_fence_wait_seq_timeout(fence->rdev, seq, intr, MAX_SCHEDULE_TIMEOUT); + if (r < 0) { return r; + } - fence->seq = RADEON_FENCE_SIGNALED_SEQ; + r = fence_signal(&fence->base); + if (!r) + FENCE_TRACE(&fence->base, "signaled from fence_wait\n"); return 0; } @@ -434,7 +581,7 @@ int radeon_fence_wait_any(struct radeon_device *rdev, { uint64_t seq[RADEON_NUM_RINGS]; unsigned i, num_rings = 0; - int r; + long r; for (i = 0; i < RADEON_NUM_RINGS; ++i) { seq[i] = 0; @@ -445,18 +592,14 @@ int radeon_fence_wait_any(struct radeon_device *rdev, seq[i] = fences[i]->seq; ++num_rings; - - /* test if something was allready signaled */ - if (seq[i] == RADEON_FENCE_SIGNALED_SEQ) - return 0; } /* nothing to wait for ? */ if (num_rings == 0) return -ENOENT; - r = radeon_fence_wait_seq(rdev, seq, intr); - if (r) { + r = radeon_fence_wait_seq_timeout(rdev, seq, intr, MAX_SCHEDULE_TIMEOUT); + if (r < 0) { return r; } return 0; @@ -475,6 +618,7 @@ int radeon_fence_wait_any(struct radeon_device *rdev, int radeon_fence_wait_next(struct radeon_device *rdev, int ring) { uint64_t seq[RADEON_NUM_RINGS] = {}; + long r; seq[ring] = atomic64_read(&rdev->fence_drv[ring].last_seq) + 1ULL; if (seq[ring] >= rdev->fence_drv[ring].sync_seq[ring]) { @@ -482,7 +626,10 @@ int radeon_fence_wait_next(struct radeon_device *rdev, int ring) already the last emited fence */ return -ENOENT; } - return radeon_fence_wait_seq(rdev, seq, false); + r = radeon_fence_wait_seq_timeout(rdev, seq, false, MAX_SCHEDULE_TIMEOUT); + if (r < 0) + return r; + return 0; } /** @@ -498,18 +645,18 @@ int radeon_fence_wait_next(struct radeon_device *rdev, int ring) int radeon_fence_wait_empty(struct radeon_device *rdev, int ring) { uint64_t seq[RADEON_NUM_RINGS] = {}; - int r; + long r; seq[ring] = rdev->fence_drv[ring].sync_seq[ring]; if (!seq[ring]) return 0; - r = radeon_fence_wait_seq(rdev, seq, false); - if (r) { + r = radeon_fence_wait_seq_timeout(rdev, seq, false, MAX_SCHEDULE_TIMEOUT); + if (r < 0) { if (r == -EDEADLK) return -EDEADLK; - dev_err(rdev->dev, "error waiting for ring[%d] to become idle (%d)\n", + dev_err(rdev->dev, "error waiting for ring[%d] to become idle (%ld)\n", ring, r); } return 0; @@ -525,7 +672,7 @@ int radeon_fence_wait_empty(struct radeon_device *rdev, int ring) */ struct radeon_fence *radeon_fence_ref(struct radeon_fence *fence) { - kref_get(&fence->kref); + fence_get(&fence->base); return fence; } @@ -542,7 +689,7 @@ void radeon_fence_unref(struct radeon_fence **fence) *fence = NULL; if (tmp) { - kref_put(&tmp->kref, radeon_fence_destroy); + fence_put(&tmp->base); } } @@ -711,6 +858,9 @@ static void radeon_fence_driver_init_ring(struct radeon_device *rdev, int ring) rdev->fence_drv[ring].sync_seq[i] = 0; atomic64_set(&rdev->fence_drv[ring].last_seq, 0); rdev->fence_drv[ring].initialized = false; + INIT_DELAYED_WORK(&rdev->fence_drv[ring].lockup_work, + radeon_fence_check_lockup); + rdev->fence_drv[ring].rdev = rdev; } /** @@ -758,8 +908,9 @@ void radeon_fence_driver_fini(struct radeon_device *rdev) r = radeon_fence_wait_empty(rdev, ring); if (r) { /* no need to trigger GPU reset as we are unloading */ - radeon_fence_driver_force_completion(rdev); + radeon_fence_driver_force_completion(rdev, ring); } + cancel_delayed_work_sync(&rdev->fence_drv[ring].lockup_work); wake_up_all(&rdev->fence_queue); radeon_scratch_free(rdev, rdev->fence_drv[ring].scratch_reg); rdev->fence_drv[ring].initialized = false; @@ -771,18 +922,16 @@ void radeon_fence_driver_fini(struct radeon_device *rdev) * radeon_fence_driver_force_completion - force all fence waiter to complete * * @rdev: radeon device pointer + * @ring: the ring to complete * * In case of GPU reset failure make sure no process keep waiting on fence * that will never complete. */ -void radeon_fence_driver_force_completion(struct radeon_device *rdev) +void radeon_fence_driver_force_completion(struct radeon_device *rdev, int ring) { - int ring; - - for (ring = 0; ring < RADEON_NUM_RINGS; ring++) { - if (!rdev->fence_drv[ring].initialized) - continue; + if (rdev->fence_drv[ring].initialized) { radeon_fence_write(rdev, rdev->fence_drv[ring].sync_seq[ring], ring); + cancel_delayed_work_sync(&rdev->fence_drv[ring].lockup_work); } } @@ -833,6 +982,7 @@ static int radeon_debugfs_gpu_reset(struct seq_file *m, void *data) down_read(&rdev->exclusive_lock); seq_printf(m, "%d\n", rdev->needs_reset); rdev->needs_reset = true; + wake_up_all(&rdev->fence_queue); up_read(&rdev->exclusive_lock); return 0; @@ -852,3 +1002,72 @@ int radeon_debugfs_fence_init(struct radeon_device *rdev) return 0; #endif } + +static const char *radeon_fence_get_driver_name(struct fence *fence) +{ + return "radeon"; +} + +static const char *radeon_fence_get_timeline_name(struct fence *f) +{ + struct radeon_fence *fence = to_radeon_fence(f); + switch (fence->ring) { + case RADEON_RING_TYPE_GFX_INDEX: return "radeon.gfx"; + case CAYMAN_RING_TYPE_CP1_INDEX: return "radeon.cp1"; + case CAYMAN_RING_TYPE_CP2_INDEX: return "radeon.cp2"; + case R600_RING_TYPE_DMA_INDEX: return "radeon.dma"; + case CAYMAN_RING_TYPE_DMA1_INDEX: return "radeon.dma1"; + case R600_RING_TYPE_UVD_INDEX: return "radeon.uvd"; + case TN_RING_TYPE_VCE1_INDEX: return "radeon.vce1"; + case TN_RING_TYPE_VCE2_INDEX: return "radeon.vce2"; + default: WARN_ON_ONCE(1); return "radeon.unk"; + } +} + +static inline bool radeon_test_signaled(struct radeon_fence *fence) +{ + return test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags); +} + +static signed long radeon_fence_default_wait(struct fence *f, bool intr, + signed long t) +{ + struct radeon_fence *fence = to_radeon_fence(f); + struct radeon_device *rdev = fence->rdev; + bool signaled; + + fence_enable_sw_signaling(&fence->base); + + /* + * This function has to return -EDEADLK, but cannot hold + * exclusive_lock during the wait because some callers + * may already hold it. This means checking needs_reset without + * lock, and not fiddling with any gpu internals. + * + * The callback installed with fence_enable_sw_signaling will + * run before our wait_event_*timeout call, so we will see + * both the signaled fence and the changes to needs_reset. + */ + + if (intr) + t = wait_event_interruptible_timeout(rdev->fence_queue, + ((signaled = radeon_test_signaled(fence)) || + rdev->needs_reset), t); + else + t = wait_event_timeout(rdev->fence_queue, + ((signaled = radeon_test_signaled(fence)) || + rdev->needs_reset), t); + + if (t > 0 && !signaled) + return -EDEADLK; + return t; +} + +const struct fence_ops radeon_fence_ops = { + .get_driver_name = radeon_fence_get_driver_name, + .get_timeline_name = radeon_fence_get_timeline_name, + .enable_signaling = radeon_fence_enable_signaling, + .signaled = radeon_fence_is_signaled, + .wait = radeon_fence_default_wait, + .release = NULL, +}; diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c index a053a0779aac35e907ab4e52ed8c71746a0c30c9..84146d5901aa5aacad168255c14d72cafce5cf15 100644 --- a/drivers/gpu/drm/radeon/radeon_gart.c +++ b/drivers/gpu/drm/radeon/radeon_gart.c @@ -128,7 +128,7 @@ int radeon_gart_table_vram_alloc(struct radeon_device *rdev) if (rdev->gart.robj == NULL) { r = radeon_bo_create(rdev, rdev->gart.table_size, PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, - 0, NULL, &rdev->gart.robj); + 0, NULL, NULL, &rdev->gart.robj); if (r) { return r; } diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index bfd7e1b0ff3f88b19a6e2d4ec636c3c8dd9c95a4..c194497aa586e16c5c17776d5baa5959f3eac7c5 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -67,7 +67,7 @@ int radeon_gem_object_create(struct radeon_device *rdev, unsigned long size, retry: r = radeon_bo_create(rdev, size, alignment, kernel, initial_domain, - flags, NULL, &robj); + flags, NULL, NULL, &robj); if (r) { if (r != -ERESTARTSYS) { if (initial_domain == RADEON_GEM_DOMAIN_VRAM) { @@ -94,7 +94,7 @@ static int radeon_gem_set_domain(struct drm_gem_object *gobj, { struct radeon_bo *robj; uint32_t domain; - int r; + long r; /* FIXME: reeimplement */ robj = gem_to_radeon_bo(gobj); @@ -110,9 +110,12 @@ static int radeon_gem_set_domain(struct drm_gem_object *gobj, } if (domain == RADEON_GEM_DOMAIN_CPU) { /* Asking for cpu access wait for object idle */ - r = radeon_bo_wait(robj, NULL, false); - if (r) { - printk(KERN_ERR "Failed to wait for object !\n"); + r = reservation_object_wait_timeout_rcu(robj->tbo.resv, true, true, 30 * HZ); + if (!r) + r = -EBUSY; + + if (r < 0 && r != -EINTR) { + printk(KERN_ERR "Failed to wait for object: %li\n", r); return r; } } @@ -272,6 +275,94 @@ int radeon_gem_create_ioctl(struct drm_device *dev, void *data, return 0; } +int radeon_gem_userptr_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct radeon_device *rdev = dev->dev_private; + struct drm_radeon_gem_userptr *args = data; + struct drm_gem_object *gobj; + struct radeon_bo *bo; + uint32_t handle; + int r; + + if (offset_in_page(args->addr | args->size)) + return -EINVAL; + + /* reject unknown flag values */ + if (args->flags & ~(RADEON_GEM_USERPTR_READONLY | + RADEON_GEM_USERPTR_ANONONLY | RADEON_GEM_USERPTR_VALIDATE | + RADEON_GEM_USERPTR_REGISTER)) + return -EINVAL; + + if (args->flags & RADEON_GEM_USERPTR_READONLY) { + /* readonly pages not tested on older hardware */ + if (rdev->family < CHIP_R600) + return -EINVAL; + + } else if (!(args->flags & RADEON_GEM_USERPTR_ANONONLY) || + !(args->flags & RADEON_GEM_USERPTR_REGISTER)) { + + /* if we want to write to it we must require anonymous + memory and install a MMU notifier */ + return -EACCES; + } + + down_read(&rdev->exclusive_lock); + + /* create a gem object to contain this object in */ + r = radeon_gem_object_create(rdev, args->size, 0, + RADEON_GEM_DOMAIN_CPU, 0, + false, &gobj); + if (r) + goto handle_lockup; + + bo = gem_to_radeon_bo(gobj); + r = radeon_ttm_tt_set_userptr(bo->tbo.ttm, args->addr, args->flags); + if (r) + goto release_object; + + if (args->flags & RADEON_GEM_USERPTR_REGISTER) { + r = radeon_mn_register(bo, args->addr); + if (r) + goto release_object; + } + + if (args->flags & RADEON_GEM_USERPTR_VALIDATE) { + down_read(¤t->mm->mmap_sem); + r = radeon_bo_reserve(bo, true); + if (r) { + up_read(¤t->mm->mmap_sem); + goto release_object; + } + + radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_GTT); + r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false); + radeon_bo_unreserve(bo); + up_read(¤t->mm->mmap_sem); + if (r) + goto release_object; + } + + r = drm_gem_handle_create(filp, gobj, &handle); + /* drop reference from allocate - handle holds it now */ + drm_gem_object_unreference_unlocked(gobj); + if (r) + goto handle_lockup; + + args->handle = handle; + up_read(&rdev->exclusive_lock); + return 0; + +release_object: + drm_gem_object_unreference_unlocked(gobj); + +handle_lockup: + up_read(&rdev->exclusive_lock); + r = radeon_gem_handle_lockup(rdev, r); + + return r; +} + int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { @@ -315,6 +406,10 @@ int radeon_mode_dumb_mmap(struct drm_file *filp, return -ENOENT; } robj = gem_to_radeon_bo(gobj); + if (radeon_ttm_tt_has_userptr(robj->tbo.ttm)) { + drm_gem_object_unreference_unlocked(gobj); + return -EPERM; + } *offset_p = radeon_bo_mmap_offset(robj); drm_gem_object_unreference_unlocked(gobj); return 0; @@ -357,15 +452,22 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data, struct drm_radeon_gem_wait_idle *args = data; struct drm_gem_object *gobj; struct radeon_bo *robj; - int r; + int r = 0; uint32_t cur_placement = 0; + long ret; gobj = drm_gem_object_lookup(dev, filp, args->handle); if (gobj == NULL) { return -ENOENT; } robj = gem_to_radeon_bo(gobj); - r = radeon_bo_wait(robj, &cur_placement, false); + + ret = reservation_object_wait_timeout_rcu(robj->tbo.resv, true, true, 30 * HZ); + if (ret == 0) + r = -EBUSY; + else if (ret < 0) + r = ret; + /* Flush HDP cache via MMIO if necessary */ if (rdev->asic->mmio_hdp_flush && radeon_mem_type_to_domain(cur_placement) == RADEON_GEM_DOMAIN_VRAM) @@ -532,6 +634,11 @@ int radeon_gem_op_ioctl(struct drm_device *dev, void *data, return -ENOENT; } robj = gem_to_radeon_bo(gobj); + + r = -EPERM; + if (radeon_ttm_tt_has_userptr(robj->tbo.ttm)) + goto out; + r = radeon_bo_reserve(robj, false); if (unlikely(r)) goto out; diff --git a/drivers/gpu/drm/radeon/radeon_ib.c b/drivers/gpu/drm/radeon/radeon_ib.c index 5bf2c0a05827f57b459ca1be85e268af0bbf32ce..3f39fcca4d0744ec666630be4e3672ada57cba08 100644 --- a/drivers/gpu/drm/radeon/radeon_ib.c +++ b/drivers/gpu/drm/radeon/radeon_ib.c @@ -145,7 +145,7 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib, if (ib->vm) { struct radeon_fence *vm_id_fence; vm_id_fence = radeon_vm_grab_id(rdev, ib->vm, ib->ring); - radeon_semaphore_sync_to(ib->semaphore, vm_id_fence); + radeon_semaphore_sync_fence(ib->semaphore, vm_id_fence); } /* sync with other rings */ @@ -269,6 +269,7 @@ int radeon_ib_ring_tests(struct radeon_device *rdev) r = radeon_ib_test(rdev, i, ring); if (r) { + radeon_fence_driver_force_completion(rdev, i); ring->ready = false; rdev->needs_reset = false; diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index 16807afab362509f8432bf4a021d3954e747c301..7784911d78ef6fc54d6aeea23950f4585d3c74c4 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -87,23 +87,6 @@ static void radeon_hotplug_work_func(struct work_struct *work) drm_helper_hpd_irq_event(dev); } -/** - * radeon_irq_reset_work_func - execute gpu reset - * - * @work: work struct - * - * Execute scheduled gpu reset (cayman+). - * This function is called when the irq handler - * thinks we need a gpu reset. - */ -static void radeon_irq_reset_work_func(struct work_struct *work) -{ - struct radeon_device *rdev = container_of(work, struct radeon_device, - reset_work); - - radeon_gpu_reset(rdev); -} - /** * radeon_driver_irq_preinstall_kms - drm irq preinstall callback * @@ -284,7 +267,6 @@ int radeon_irq_kms_init(struct radeon_device *rdev) INIT_WORK(&rdev->hotplug_work, radeon_hotplug_work_func); INIT_WORK(&rdev->audio_work, r600_audio_update_hdmi); - INIT_WORK(&rdev->reset_work, radeon_irq_reset_work_func); rdev->irq.installed = true; r = drm_irq_install(rdev->ddev, rdev->ddev->pdev->irq); @@ -341,6 +323,21 @@ void radeon_irq_kms_sw_irq_get(struct radeon_device *rdev, int ring) } } +/** + * radeon_irq_kms_sw_irq_get_delayed - enable software interrupt + * + * @rdev: radeon device pointer + * @ring: ring whose interrupt you want to enable + * + * Enables the software interrupt for a specific ring (all asics). + * The software interrupt is generally used to signal a fence on + * a particular ring. + */ +bool radeon_irq_kms_sw_irq_get_delayed(struct radeon_device *rdev, int ring) +{ + return atomic_inc_return(&rdev->irq.ring_int[ring]) == 1; +} + /** * radeon_irq_kms_sw_irq_put - disable software interrupt * diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index eb7164d07985668a7c698a44d4d2e9e29668dfb8..8309b11e674d8506bfeb3f05530407ede4b60642 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -885,5 +885,6 @@ const struct drm_ioctl_desc radeon_ioctls_kms[] = { DRM_IOCTL_DEF_DRV(RADEON_GEM_BUSY, radeon_gem_busy_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(RADEON_GEM_VA, radeon_gem_va_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(RADEON_GEM_OP, radeon_gem_op_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(RADEON_GEM_USERPTR, radeon_gem_userptr_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), }; int radeon_max_kms_ioctl = ARRAY_SIZE(radeon_ioctls_kms); diff --git a/drivers/gpu/drm/radeon/radeon_mn.c b/drivers/gpu/drm/radeon/radeon_mn.c new file mode 100644 index 0000000000000000000000000000000000000000..a69bd441dd2d0cc612b9fa4d9e7711934270e3c5 --- /dev/null +++ b/drivers/gpu/drm/radeon/radeon_mn.c @@ -0,0 +1,274 @@ +/* + * Copyright 2014 Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: + * Christian König + */ + +#include +#include +#include +#include +#include + +#include "radeon.h" + +struct radeon_mn { + /* constant after initialisation */ + struct radeon_device *rdev; + struct mm_struct *mm; + struct mmu_notifier mn; + + /* only used on destruction */ + struct work_struct work; + + /* protected by rdev->mn_lock */ + struct hlist_node node; + + /* objects protected by lock */ + struct mutex lock; + struct rb_root objects; +}; + +/** + * radeon_mn_destroy - destroy the rmn + * + * @work: previously sheduled work item + * + * Lazy destroys the notifier from a work item + */ +static void radeon_mn_destroy(struct work_struct *work) +{ + struct radeon_mn *rmn = container_of(work, struct radeon_mn, work); + struct radeon_device *rdev = rmn->rdev; + struct radeon_bo *bo, *next; + + mutex_lock(&rdev->mn_lock); + mutex_lock(&rmn->lock); + hash_del(&rmn->node); + rbtree_postorder_for_each_entry_safe(bo, next, &rmn->objects, mn_it.rb) { + interval_tree_remove(&bo->mn_it, &rmn->objects); + bo->mn = NULL; + } + mutex_unlock(&rmn->lock); + mutex_unlock(&rdev->mn_lock); + mmu_notifier_unregister(&rmn->mn, rmn->mm); + kfree(rmn); +} + +/** + * radeon_mn_release - callback to notify about mm destruction + * + * @mn: our notifier + * @mn: the mm this callback is about + * + * Shedule a work item to lazy destroy our notifier. + */ +static void radeon_mn_release(struct mmu_notifier *mn, + struct mm_struct *mm) +{ + struct radeon_mn *rmn = container_of(mn, struct radeon_mn, mn); + INIT_WORK(&rmn->work, radeon_mn_destroy); + schedule_work(&rmn->work); +} + +/** + * radeon_mn_invalidate_range_start - callback to notify about mm change + * + * @mn: our notifier + * @mn: the mm this callback is about + * @start: start of updated range + * @end: end of updated range + * + * We block for all BOs between start and end to be idle and + * unmap them by move them into system domain again. + */ +static void radeon_mn_invalidate_range_start(struct mmu_notifier *mn, + struct mm_struct *mm, + unsigned long start, + unsigned long end) +{ + struct radeon_mn *rmn = container_of(mn, struct radeon_mn, mn); + struct interval_tree_node *it; + + /* notification is exclusive, but interval is inclusive */ + end -= 1; + + mutex_lock(&rmn->lock); + + it = interval_tree_iter_first(&rmn->objects, start, end); + while (it) { + struct radeon_bo *bo; + struct fence *fence; + int r; + + bo = container_of(it, struct radeon_bo, mn_it); + it = interval_tree_iter_next(it, start, end); + + r = radeon_bo_reserve(bo, true); + if (r) { + DRM_ERROR("(%d) failed to reserve user bo\n", r); + continue; + } + + fence = reservation_object_get_excl(bo->tbo.resv); + if (fence) { + r = radeon_fence_wait((struct radeon_fence *)fence, false); + if (r) + DRM_ERROR("(%d) failed to wait for user bo\n", r); + } + + radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_CPU); + r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); + if (r) + DRM_ERROR("(%d) failed to validate user bo\n", r); + + radeon_bo_unreserve(bo); + } + + mutex_unlock(&rmn->lock); +} + +static const struct mmu_notifier_ops radeon_mn_ops = { + .release = radeon_mn_release, + .invalidate_range_start = radeon_mn_invalidate_range_start, +}; + +/** + * radeon_mn_get - create notifier context + * + * @rdev: radeon device pointer + * + * Creates a notifier context for current->mm. + */ +static struct radeon_mn *radeon_mn_get(struct radeon_device *rdev) +{ + struct mm_struct *mm = current->mm; + struct radeon_mn *rmn; + int r; + + down_write(&mm->mmap_sem); + mutex_lock(&rdev->mn_lock); + + hash_for_each_possible(rdev->mn_hash, rmn, node, (unsigned long)mm) + if (rmn->mm == mm) + goto release_locks; + + rmn = kzalloc(sizeof(*rmn), GFP_KERNEL); + if (!rmn) { + rmn = ERR_PTR(-ENOMEM); + goto release_locks; + } + + rmn->rdev = rdev; + rmn->mm = mm; + rmn->mn.ops = &radeon_mn_ops; + mutex_init(&rmn->lock); + rmn->objects = RB_ROOT; + + r = __mmu_notifier_register(&rmn->mn, mm); + if (r) + goto free_rmn; + + hash_add(rdev->mn_hash, &rmn->node, (unsigned long)mm); + +release_locks: + mutex_unlock(&rdev->mn_lock); + up_write(&mm->mmap_sem); + + return rmn; + +free_rmn: + mutex_unlock(&rdev->mn_lock); + up_write(&mm->mmap_sem); + kfree(rmn); + + return ERR_PTR(r); +} + +/** + * radeon_mn_register - register a BO for notifier updates + * + * @bo: radeon buffer object + * @addr: userptr addr we should monitor + * + * Registers an MMU notifier for the given BO at the specified address. + * Returns 0 on success, -ERRNO if anything goes wrong. + */ +int radeon_mn_register(struct radeon_bo *bo, unsigned long addr) +{ + unsigned long end = addr + radeon_bo_size(bo) - 1; + struct radeon_device *rdev = bo->rdev; + struct radeon_mn *rmn; + struct interval_tree_node *it; + + rmn = radeon_mn_get(rdev); + if (IS_ERR(rmn)) + return PTR_ERR(rmn); + + mutex_lock(&rmn->lock); + + it = interval_tree_iter_first(&rmn->objects, addr, end); + if (it) { + mutex_unlock(&rmn->lock); + return -EEXIST; + } + + bo->mn = rmn; + bo->mn_it.start = addr; + bo->mn_it.last = end; + interval_tree_insert(&bo->mn_it, &rmn->objects); + + mutex_unlock(&rmn->lock); + + return 0; +} + +/** + * radeon_mn_unregister - unregister a BO for notifier updates + * + * @bo: radeon buffer object + * + * Remove any registration of MMU notifier updates from the buffer object. + */ +void radeon_mn_unregister(struct radeon_bo *bo) +{ + struct radeon_device *rdev = bo->rdev; + struct radeon_mn *rmn; + + mutex_lock(&rdev->mn_lock); + rmn = bo->mn; + if (rmn == NULL) { + mutex_unlock(&rdev->mn_lock); + return; + } + + mutex_lock(&rmn->lock); + interval_tree_remove(&bo->mn_it, &rmn->objects); + bo->mn = NULL; + mutex_unlock(&rmn->lock); + mutex_unlock(&rdev->mn_lock); +} diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index e27608c29c112f5e8d164214ca7b56855ef15593..04db2fdd869271f81482e75913d6ec4002ffecac 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -777,6 +777,7 @@ extern void atombios_digital_setup(struct drm_encoder *encoder, int action); extern int atombios_get_encoder_mode(struct drm_encoder *encoder); extern bool atombios_set_edp_panel_power(struct drm_connector *connector, int action); extern void radeon_encoder_set_active_device(struct drm_encoder *encoder); +extern bool radeon_encoder_is_digital(struct drm_encoder *encoder); extern void radeon_crtc_load_lut(struct drm_crtc *crtc); extern int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y, diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 480c87d8edc50cc46aad4c612b1f6f4942d9a8a1..99a960a4f30265c893a411763843a260d38e6a41 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -75,6 +75,7 @@ static void radeon_ttm_bo_destroy(struct ttm_buffer_object *tbo) bo = container_of(tbo, struct radeon_bo, tbo); radeon_update_memory_usage(bo, bo->tbo.mem.mem_type, -1); + radeon_mn_unregister(bo); mutex_lock(&bo->rdev->gem.mutex); list_del_init(&bo->list); @@ -96,55 +97,80 @@ void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain) { u32 c = 0, i; - rbo->placement.fpfn = 0; - rbo->placement.lpfn = 0; rbo->placement.placement = rbo->placements; rbo->placement.busy_placement = rbo->placements; if (domain & RADEON_GEM_DOMAIN_VRAM) - rbo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | - TTM_PL_FLAG_VRAM; + rbo->placements[c++].flags = TTM_PL_FLAG_WC | + TTM_PL_FLAG_UNCACHED | + TTM_PL_FLAG_VRAM; + if (domain & RADEON_GEM_DOMAIN_GTT) { if (rbo->flags & RADEON_GEM_GTT_UC) { - rbo->placements[c++] = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_TT; + rbo->placements[c++].flags = TTM_PL_FLAG_UNCACHED | + TTM_PL_FLAG_TT; + } else if ((rbo->flags & RADEON_GEM_GTT_WC) || (rbo->rdev->flags & RADEON_IS_AGP)) { - rbo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | + rbo->placements[c++].flags = TTM_PL_FLAG_WC | + TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_TT; } else { - rbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_TT; + rbo->placements[c++].flags = TTM_PL_FLAG_CACHED | + TTM_PL_FLAG_TT; } } + if (domain & RADEON_GEM_DOMAIN_CPU) { if (rbo->flags & RADEON_GEM_GTT_UC) { - rbo->placements[c++] = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_SYSTEM; + rbo->placements[c++].flags = TTM_PL_FLAG_UNCACHED | + TTM_PL_FLAG_SYSTEM; + } else if ((rbo->flags & RADEON_GEM_GTT_WC) || rbo->rdev->flags & RADEON_IS_AGP) { - rbo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | + rbo->placements[c++].flags = TTM_PL_FLAG_WC | + TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_SYSTEM; } else { - rbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM; + rbo->placements[c++].flags = TTM_PL_FLAG_CACHED | + TTM_PL_FLAG_SYSTEM; } } if (!c) - rbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + rbo->placements[c++].flags = TTM_PL_MASK_CACHING | + TTM_PL_FLAG_SYSTEM; + rbo->placement.num_placement = c; rbo->placement.num_busy_placement = c; + for (i = 0; i < c; ++i) { + rbo->placements[i].fpfn = 0; + if ((rbo->flags & RADEON_GEM_CPU_ACCESS) && + (rbo->placements[i].flags & TTM_PL_FLAG_VRAM)) + rbo->placements[i].lpfn = + rbo->rdev->mc.visible_vram_size >> PAGE_SHIFT; + else + rbo->placements[i].lpfn = 0; + } + /* * Use two-ended allocation depending on the buffer size to * improve fragmentation quality. * 512kb was measured as the most optimal number. */ - if (rbo->tbo.mem.size > 512 * 1024) { + if (!((rbo->flags & RADEON_GEM_CPU_ACCESS) && + (rbo->placements[i].flags & TTM_PL_FLAG_VRAM)) && + rbo->tbo.mem.size > 512 * 1024) { for (i = 0; i < c; i++) { - rbo->placements[i] |= TTM_PL_FLAG_TOPDOWN; + rbo->placements[i].flags |= TTM_PL_FLAG_TOPDOWN; } } } int radeon_bo_create(struct radeon_device *rdev, - unsigned long size, int byte_align, bool kernel, u32 domain, - u32 flags, struct sg_table *sg, struct radeon_bo **bo_ptr) + unsigned long size, int byte_align, bool kernel, + u32 domain, u32 flags, struct sg_table *sg, + struct reservation_object *resv, + struct radeon_bo **bo_ptr) { struct radeon_bo *bo; enum ttm_bo_type type; @@ -192,7 +218,7 @@ int radeon_bo_create(struct radeon_device *rdev, down_read(&rdev->pm.mclk_lock); r = ttm_bo_init(&rdev->mman.bdev, &bo->tbo, size, type, &bo->placement, page_align, !kernel, NULL, - acc_size, sg, &radeon_ttm_bo_destroy); + acc_size, sg, resv, &radeon_ttm_bo_destroy); up_read(&rdev->pm.mclk_lock); if (unlikely(r != 0)) { return r; @@ -264,6 +290,9 @@ int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain, u64 max_offset, { int r, i; + if (radeon_ttm_tt_has_userptr(bo->tbo.ttm)) + return -EPERM; + if (bo->pin_count) { bo->pin_count++; if (gpu_addr) @@ -283,21 +312,19 @@ int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain, u64 max_offset, return 0; } radeon_ttm_placement_from_domain(bo, domain); - if (domain == RADEON_GEM_DOMAIN_VRAM) { + for (i = 0; i < bo->placement.num_placement; i++) { /* force to pin into visible video ram */ - bo->placement.lpfn = bo->rdev->mc.visible_vram_size >> PAGE_SHIFT; - } - if (max_offset) { - u64 lpfn = max_offset >> PAGE_SHIFT; - - if (!bo->placement.lpfn) - bo->placement.lpfn = bo->rdev->mc.gtt_size >> PAGE_SHIFT; + if ((bo->placements[i].flags & TTM_PL_FLAG_VRAM) && + !(bo->flags & RADEON_GEM_NO_CPU_ACCESS) && + (!max_offset || max_offset > bo->rdev->mc.visible_vram_size)) + bo->placements[i].lpfn = + bo->rdev->mc.visible_vram_size >> PAGE_SHIFT; + else + bo->placements[i].lpfn = max_offset >> PAGE_SHIFT; - if (lpfn < bo->placement.lpfn) - bo->placement.lpfn = lpfn; + bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; } - for (i = 0; i < bo->placement.num_placement; i++) - bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); if (likely(r == 0)) { bo->pin_count = 1; @@ -329,8 +356,10 @@ int radeon_bo_unpin(struct radeon_bo *bo) bo->pin_count--; if (bo->pin_count) return 0; - for (i = 0; i < bo->placement.num_placement; i++) - bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT; + for (i = 0; i < bo->placement.num_placement; i++) { + bo->placements[i].lpfn = 0; + bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; + } r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); if (likely(r == 0)) { if (bo->tbo.mem.mem_type == TTM_PL_VRAM) @@ -459,7 +488,7 @@ int radeon_bo_list_validate(struct radeon_device *rdev, u64 bytes_moved = 0, initial_bytes_moved; u64 bytes_moved_threshold = radeon_bo_get_threshold_for_moves(rdev); - r = ttm_eu_reserve_buffers(ticket, head); + r = ttm_eu_reserve_buffers(ticket, head, true); if (unlikely(r != 0)) { return r; } @@ -468,6 +497,7 @@ int radeon_bo_list_validate(struct radeon_device *rdev, bo = lobj->robj; if (!bo->pin_count) { u32 domain = lobj->prefered_domains; + u32 allowed = lobj->allowed_domains; u32 current_domain = radeon_mem_type_to_domain(bo->tbo.mem.mem_type); @@ -479,7 +509,7 @@ int radeon_bo_list_validate(struct radeon_device *rdev, * into account. We don't want to disallow buffer moves * completely. */ - if ((lobj->allowed_domains & current_domain) != 0 && + if ((allowed & current_domain) != 0 && (domain & current_domain) == 0 && /* will be moved */ bytes_moved > bytes_moved_threshold) { /* don't move it */ @@ -489,7 +519,7 @@ int radeon_bo_list_validate(struct radeon_device *rdev, retry: radeon_ttm_placement_from_domain(bo, domain); if (ring == R600_RING_TYPE_UVD_INDEX) - radeon_uvd_force_into_uvd_segment(bo); + radeon_uvd_force_into_uvd_segment(bo, allowed); initial_bytes_moved = atomic64_read(&rdev->num_bytes_moved); r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false); @@ -731,7 +761,7 @@ int radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo) /* hurrah the memory is not visible ! */ radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_VRAM); - rbo->placement.lpfn = rdev->mc.visible_vram_size >> PAGE_SHIFT; + rbo->placements[0].lpfn = rdev->mc.visible_vram_size >> PAGE_SHIFT; r = ttm_bo_validate(bo, &rbo->placement, false, false); if (unlikely(r == -ENOMEM)) { radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_GTT); @@ -755,12 +785,10 @@ int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type, bool no_wait) r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL); if (unlikely(r != 0)) return r; - spin_lock(&bo->tbo.bdev->fence_lock); if (mem_type) *mem_type = bo->tbo.mem.mem_type; - if (bo->tbo.sync_obj) - r = ttm_bo_wait(&bo->tbo, true, true, no_wait); - spin_unlock(&bo->tbo.bdev->fence_lock); + + r = ttm_bo_wait(&bo->tbo, true, true, no_wait); ttm_bo_unreserve(&bo->tbo); return r; } diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h index 98a47fdf362510284d21b864f1e5e565b5388604..1b8ec7917154809e10336fb346c6aacbafa72939 100644 --- a/drivers/gpu/drm/radeon/radeon_object.h +++ b/drivers/gpu/drm/radeon/radeon_object.h @@ -126,6 +126,7 @@ extern int radeon_bo_create(struct radeon_device *rdev, unsigned long size, int byte_align, bool kernel, u32 domain, u32 flags, struct sg_table *sg, + struct reservation_object *resv, struct radeon_bo **bo_ptr); extern int radeon_bo_kmap(struct radeon_bo *bo, void **ptr); extern void radeon_bo_kunmap(struct radeon_bo *bo); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 164898b0010c68b350c175b705ef3534af5be69a..32522cc940a127c4b413c9928121c2b9258457fd 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1556,7 +1556,7 @@ static bool radeon_pm_in_vbl(struct radeon_device *rdev) if (rdev->pm.active_crtcs & (1 << crtc)) { vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, crtc, 0, &vpos, &hpos, NULL, NULL); if ((vbl_status & DRM_SCANOUTPOS_VALID) && - !(vbl_status & DRM_SCANOUTPOS_INVBL)) + !(vbl_status & DRM_SCANOUTPOS_IN_VBLANK)) in_vbl = false; } } diff --git a/drivers/gpu/drm/radeon/radeon_prime.c b/drivers/gpu/drm/radeon/radeon_prime.c index 0b16f2cbcf170b5a9a6d23b6683eedb827b890b4..f3609c97496b2f56bf35cfbe2cf52f76b30b9f79 100644 --- a/drivers/gpu/drm/radeon/radeon_prime.c +++ b/drivers/gpu/drm/radeon/radeon_prime.c @@ -27,6 +27,7 @@ #include "radeon.h" #include +#include struct sg_table *radeon_gem_prime_get_sg_table(struct drm_gem_object *obj) { @@ -57,15 +58,18 @@ void radeon_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) } struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev, - size_t size, + struct dma_buf_attachment *attach, struct sg_table *sg) { + struct reservation_object *resv = attach->dmabuf->resv; struct radeon_device *rdev = dev->dev_private; struct radeon_bo *bo; int ret; - ret = radeon_bo_create(rdev, size, PAGE_SIZE, false, - RADEON_GEM_DOMAIN_GTT, 0, sg, &bo); + ww_mutex_lock(&resv->lock, NULL); + ret = radeon_bo_create(rdev, attach->dmabuf->size, PAGE_SIZE, false, + RADEON_GEM_DOMAIN_GTT, 0, sg, resv, &bo); + ww_mutex_unlock(&resv->lock); if (ret) return ERR_PTR(ret); @@ -111,3 +115,13 @@ struct reservation_object *radeon_gem_prime_res_obj(struct drm_gem_object *obj) return bo->tbo.resv; } + +struct dma_buf *radeon_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *gobj, + int flags) +{ + struct radeon_bo *bo = gem_to_radeon_bo(gobj); + if (radeon_ttm_tt_has_userptr(bo->tbo.ttm)) + return ERR_PTR(-EPERM); + return drm_gem_prime_export(dev, gobj, flags); +} diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index d65607902537f41a7ede21261eb111ddbf257e63..3d17af34afa73265eea6ed31210e265acae6eb1a 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -44,27 +44,6 @@ */ static int radeon_debugfs_ring_init(struct radeon_device *rdev, struct radeon_ring *ring); -/** - * radeon_ring_write - write a value to the ring - * - * @ring: radeon_ring structure holding ring information - * @v: dword (dw) value to write - * - * Write a value to the requested ring buffer (all asics). - */ -void radeon_ring_write(struct radeon_ring *ring, uint32_t v) -{ -#if DRM_DEBUG_CODE - if (ring->count_dw <= 0) { - DRM_ERROR("radeon: writing more dwords to the ring than expected!\n"); - } -#endif - ring->ring[ring->wptr++] = v; - ring->wptr &= ring->ptr_mask; - ring->count_dw--; - ring->ring_free_dw--; -} - /** * radeon_ring_supports_scratch_reg - check if the ring supports * writing to scratch registers @@ -404,7 +383,7 @@ int radeon_ring_init(struct radeon_device *rdev, struct radeon_ring *ring, unsig /* Allocate ring buffer */ if (ring->ring_obj == NULL) { r = radeon_bo_create(rdev, ring->ring_size, PAGE_SIZE, true, - RADEON_GEM_DOMAIN_GTT, 0, + RADEON_GEM_DOMAIN_GTT, 0, NULL, NULL, &ring->ring_obj); if (r) { dev_err(rdev->dev, "(%d) ring create failed\n", r); diff --git a/drivers/gpu/drm/radeon/radeon_sa.c b/drivers/gpu/drm/radeon/radeon_sa.c index b84f97c8718cd1b7b1f46137b1646af1691f8d4e..c507896aca45a43a3fe271b79b84e7db61bfb85a 100644 --- a/drivers/gpu/drm/radeon/radeon_sa.c +++ b/drivers/gpu/drm/radeon/radeon_sa.c @@ -65,7 +65,7 @@ int radeon_sa_bo_manager_init(struct radeon_device *rdev, } r = radeon_bo_create(rdev, size, align, true, - domain, flags, NULL, &sa_manager->bo); + domain, flags, NULL, NULL, &sa_manager->bo); if (r) { dev_err(rdev->dev, "(%d) failed to allocate bo for manager\n", r); return r; diff --git a/drivers/gpu/drm/radeon/radeon_semaphore.c b/drivers/gpu/drm/radeon/radeon_semaphore.c index abd6753a570afa119ac1e68255231c99f7b27995..6deb08f045b758a9cbaefec886177b86c950a567 100644 --- a/drivers/gpu/drm/radeon/radeon_semaphore.c +++ b/drivers/gpu/drm/radeon/radeon_semaphore.c @@ -96,15 +96,15 @@ bool radeon_semaphore_emit_wait(struct radeon_device *rdev, int ridx, } /** - * radeon_semaphore_sync_to - use the semaphore to sync to a fence + * radeon_semaphore_sync_fence - use the semaphore to sync to a fence * * @semaphore: semaphore object to add fence to * @fence: fence to sync to * * Sync to the fence using this semaphore object */ -void radeon_semaphore_sync_to(struct radeon_semaphore *semaphore, - struct radeon_fence *fence) +void radeon_semaphore_sync_fence(struct radeon_semaphore *semaphore, + struct radeon_fence *fence) { struct radeon_fence *other; @@ -115,6 +115,53 @@ void radeon_semaphore_sync_to(struct radeon_semaphore *semaphore, semaphore->sync_to[fence->ring] = radeon_fence_later(fence, other); } +/** + * radeon_semaphore_sync_to - use the semaphore to sync to a reservation object + * + * @sema: semaphore object to add fence from reservation object to + * @resv: reservation object with embedded fence + * @shared: true if we should onyl sync to the exclusive fence + * + * Sync to the fence using this semaphore object + */ +int radeon_semaphore_sync_resv(struct radeon_device *rdev, + struct radeon_semaphore *sema, + struct reservation_object *resv, + bool shared) +{ + struct reservation_object_list *flist; + struct fence *f; + struct radeon_fence *fence; + unsigned i; + int r = 0; + + /* always sync to the exclusive fence */ + f = reservation_object_get_excl(resv); + fence = f ? to_radeon_fence(f) : NULL; + if (fence && fence->rdev == rdev) + radeon_semaphore_sync_fence(sema, fence); + else if (f) + r = fence_wait(f, true); + + flist = reservation_object_get_list(resv); + if (shared || !flist || r) + return r; + + for (i = 0; i < flist->shared_count; ++i) { + f = rcu_dereference_protected(flist->shared[i], + reservation_object_held(resv)); + fence = to_radeon_fence(f); + if (fence && fence->rdev == rdev) + radeon_semaphore_sync_fence(sema, fence); + else + r = fence_wait(f, true); + + if (r) + break; + } + return r; +} + /** * radeon_semaphore_sync_rings - sync ring to all registered fences * diff --git a/drivers/gpu/drm/radeon/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c index 23bb64fd775f674cae5091013b4d63979f62ebeb..535403e0c8a28c20011261decb68eeb3d47b6846 100644 --- a/drivers/gpu/drm/radeon/radeon_state.c +++ b/drivers/gpu/drm/radeon/radeon_state.c @@ -30,9 +30,9 @@ */ #include -#include #include #include "radeon_drv.h" +#include "drm_buffer.h" /* ================================================================ * Helper functions for client state checking and fixup diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c index 17bc3dced9f1db921a11f80b1167f520261ed0b1..07b506b410080f482b4d41948c3bd627ae4bb39e 100644 --- a/drivers/gpu/drm/radeon/radeon_test.c +++ b/drivers/gpu/drm/radeon/radeon_test.c @@ -67,7 +67,7 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag) } r = radeon_bo_create(rdev, size, PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, - 0, NULL, &vram_obj); + 0, NULL, NULL, &vram_obj); if (r) { DRM_ERROR("Failed to create VRAM object\n"); goto out_cleanup; @@ -87,7 +87,8 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag) struct radeon_fence *fence = NULL; r = radeon_bo_create(rdev, size, PAGE_SIZE, true, - RADEON_GEM_DOMAIN_GTT, 0, NULL, gtt_obj + i); + RADEON_GEM_DOMAIN_GTT, 0, NULL, NULL, + gtt_obj + i); if (r) { DRM_ERROR("Failed to create GTT object %d\n", i); goto out_lclean; @@ -116,11 +117,16 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag) radeon_bo_kunmap(gtt_obj[i]); if (ring == R600_RING_TYPE_DMA_INDEX) - r = radeon_copy_dma(rdev, gtt_addr, vram_addr, size / RADEON_GPU_PAGE_SIZE, &fence); + fence = radeon_copy_dma(rdev, gtt_addr, vram_addr, + size / RADEON_GPU_PAGE_SIZE, + NULL); else - r = radeon_copy_blit(rdev, gtt_addr, vram_addr, size / RADEON_GPU_PAGE_SIZE, &fence); - if (r) { + fence = radeon_copy_blit(rdev, gtt_addr, vram_addr, + size / RADEON_GPU_PAGE_SIZE, + NULL); + if (IS_ERR(fence)) { DRM_ERROR("Failed GTT->VRAM copy %d\n", i); + r = PTR_ERR(fence); goto out_lclean_unpin; } @@ -162,11 +168,16 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag) radeon_bo_kunmap(vram_obj); if (ring == R600_RING_TYPE_DMA_INDEX) - r = radeon_copy_dma(rdev, vram_addr, gtt_addr, size / RADEON_GPU_PAGE_SIZE, &fence); + fence = radeon_copy_dma(rdev, vram_addr, gtt_addr, + size / RADEON_GPU_PAGE_SIZE, + NULL); else - r = radeon_copy_blit(rdev, vram_addr, gtt_addr, size / RADEON_GPU_PAGE_SIZE, &fence); - if (r) { + fence = radeon_copy_blit(rdev, vram_addr, gtt_addr, + size / RADEON_GPU_PAGE_SIZE, + NULL); + if (IS_ERR(fence)) { DRM_ERROR("Failed VRAM->GTT copy %d\n", i); + r = PTR_ERR(fence); goto out_lclean_unpin; } @@ -222,7 +233,7 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag) radeon_bo_unreserve(gtt_obj[i]); radeon_bo_unref(>t_obj[i]); } - if (fence) + if (fence && !IS_ERR(fence)) radeon_fence_unref(&fence); break; } diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 72afe82a95c906f58387196390258b5ebb902b4e..8624979afb65f55ecba4ffb888aa09d0b3c8b39d 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include #include "radeon_reg.h" #include "radeon.h" @@ -176,12 +178,15 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, static void radeon_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *placement) { + static struct ttm_place placements = { + .fpfn = 0, + .lpfn = 0, + .flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM + }; + struct radeon_bo *rbo; - static u32 placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; if (!radeon_ttm_bo_is_radeon_bo(bo)) { - placement->fpfn = 0; - placement->lpfn = 0; placement->placement = &placements; placement->busy_placement = &placements; placement->num_placement = 1; @@ -228,6 +233,7 @@ static int radeon_move_blit(struct ttm_buffer_object *bo, struct radeon_device *rdev; uint64_t old_start, new_start; struct radeon_fence *fence; + unsigned num_pages; int r, ridx; rdev = radeon_get_rdev(bo->bdev); @@ -264,13 +270,12 @@ static int radeon_move_blit(struct ttm_buffer_object *bo, BUILD_BUG_ON((PAGE_SIZE % RADEON_GPU_PAGE_SIZE) != 0); - /* sync other rings */ - fence = bo->sync_obj; - r = radeon_copy(rdev, old_start, new_start, - new_mem->num_pages * (PAGE_SIZE / RADEON_GPU_PAGE_SIZE), /* GPU pages */ - &fence); - /* FIXME: handle copy error */ - r = ttm_bo_move_accel_cleanup(bo, (void *)fence, + num_pages = new_mem->num_pages * (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); + fence = radeon_copy(rdev, old_start, new_start, num_pages, bo->resv); + if (IS_ERR(fence)) + return PTR_ERR(fence); + + r = ttm_bo_move_accel_cleanup(bo, &fence->base, evict, no_wait_gpu, new_mem); radeon_fence_unref(&fence); return r; @@ -284,20 +289,20 @@ static int radeon_move_vram_ram(struct ttm_buffer_object *bo, struct radeon_device *rdev; struct ttm_mem_reg *old_mem = &bo->mem; struct ttm_mem_reg tmp_mem; - u32 placements; + struct ttm_place placements; struct ttm_placement placement; int r; rdev = radeon_get_rdev(bo->bdev); tmp_mem = *new_mem; tmp_mem.mm_node = NULL; - placement.fpfn = 0; - placement.lpfn = 0; placement.num_placement = 1; placement.placement = &placements; placement.num_busy_placement = 1; placement.busy_placement = &placements; - placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT; + placements.fpfn = 0; + placements.lpfn = 0; + placements.flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT; r = ttm_bo_mem_space(bo, &placement, &tmp_mem, interruptible, no_wait_gpu); if (unlikely(r)) { @@ -332,19 +337,19 @@ static int radeon_move_ram_vram(struct ttm_buffer_object *bo, struct ttm_mem_reg *old_mem = &bo->mem; struct ttm_mem_reg tmp_mem; struct ttm_placement placement; - u32 placements; + struct ttm_place placements; int r; rdev = radeon_get_rdev(bo->bdev); tmp_mem = *new_mem; tmp_mem.mm_node = NULL; - placement.fpfn = 0; - placement.lpfn = 0; placement.num_placement = 1; placement.placement = &placements; placement.num_busy_placement = 1; placement.busy_placement = &placements; - placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT; + placements.fpfn = 0; + placements.lpfn = 0; + placements.flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT; r = ttm_bo_mem_space(bo, &placement, &tmp_mem, interruptible, no_wait_gpu); if (unlikely(r)) { @@ -483,39 +488,108 @@ static void radeon_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_re { } -static int radeon_sync_obj_wait(void *sync_obj, bool lazy, bool interruptible) -{ - return radeon_fence_wait((struct radeon_fence *)sync_obj, interruptible); -} +/* + * TTM backend functions. + */ +struct radeon_ttm_tt { + struct ttm_dma_tt ttm; + struct radeon_device *rdev; + u64 offset; -static int radeon_sync_obj_flush(void *sync_obj) + uint64_t userptr; + struct mm_struct *usermm; + uint32_t userflags; +}; + +/* prepare the sg table with the user pages */ +static int radeon_ttm_tt_pin_userptr(struct ttm_tt *ttm) { + struct radeon_device *rdev = radeon_get_rdev(ttm->bdev); + struct radeon_ttm_tt *gtt = (void *)ttm; + unsigned pinned = 0, nents; + int r; + + int write = !(gtt->userflags & RADEON_GEM_USERPTR_READONLY); + enum dma_data_direction direction = write ? + DMA_BIDIRECTIONAL : DMA_TO_DEVICE; + + if (current->mm != gtt->usermm) + return -EPERM; + + if (gtt->userflags & RADEON_GEM_USERPTR_ANONONLY) { + /* check that we only pin down anonymous memory + to prevent problems with writeback */ + unsigned long end = gtt->userptr + ttm->num_pages * PAGE_SIZE; + struct vm_area_struct *vma; + vma = find_vma(gtt->usermm, gtt->userptr); + if (!vma || vma->vm_file || vma->vm_end < end) + return -EPERM; + } + + do { + unsigned num_pages = ttm->num_pages - pinned; + uint64_t userptr = gtt->userptr + pinned * PAGE_SIZE; + struct page **pages = ttm->pages + pinned; + + r = get_user_pages(current, current->mm, userptr, num_pages, + write, 0, pages, NULL); + if (r < 0) + goto release_pages; + + pinned += r; + + } while (pinned < ttm->num_pages); + + r = sg_alloc_table_from_pages(ttm->sg, ttm->pages, ttm->num_pages, 0, + ttm->num_pages << PAGE_SHIFT, + GFP_KERNEL); + if (r) + goto release_sg; + + r = -ENOMEM; + nents = dma_map_sg(rdev->dev, ttm->sg->sgl, ttm->sg->nents, direction); + if (nents != ttm->sg->nents) + goto release_sg; + + drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages, + gtt->ttm.dma_address, ttm->num_pages); + return 0; -} -static void radeon_sync_obj_unref(void **sync_obj) -{ - radeon_fence_unref((struct radeon_fence **)sync_obj); -} +release_sg: + kfree(ttm->sg); -static void *radeon_sync_obj_ref(void *sync_obj) -{ - return radeon_fence_ref((struct radeon_fence *)sync_obj); +release_pages: + release_pages(ttm->pages, pinned, 0); + return r; } -static bool radeon_sync_obj_signaled(void *sync_obj) +static void radeon_ttm_tt_unpin_userptr(struct ttm_tt *ttm) { - return radeon_fence_signaled((struct radeon_fence *)sync_obj); -} + struct radeon_device *rdev = radeon_get_rdev(ttm->bdev); + struct radeon_ttm_tt *gtt = (void *)ttm; + struct scatterlist *sg; + int i; -/* - * TTM backend functions. - */ -struct radeon_ttm_tt { - struct ttm_dma_tt ttm; - struct radeon_device *rdev; - u64 offset; -}; + int write = !(gtt->userflags & RADEON_GEM_USERPTR_READONLY); + enum dma_data_direction direction = write ? + DMA_BIDIRECTIONAL : DMA_TO_DEVICE; + + /* free the sg table and pages again */ + dma_unmap_sg(rdev->dev, ttm->sg->sgl, ttm->sg->nents, direction); + + for_each_sg(ttm->sg->sgl, sg, ttm->sg->nents, i) { + struct page *page = sg_page(sg); + + if (!(gtt->userflags & RADEON_GEM_USERPTR_READONLY)) + set_page_dirty(page); + + mark_page_accessed(page); + page_cache_release(page); + } + + sg_free_table(ttm->sg); +} static int radeon_ttm_backend_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem) @@ -525,6 +599,11 @@ static int radeon_ttm_backend_bind(struct ttm_tt *ttm, RADEON_GART_PAGE_WRITE; int r; + if (gtt->userptr) { + radeon_ttm_tt_pin_userptr(ttm); + flags &= ~RADEON_GART_PAGE_WRITE; + } + gtt->offset = (unsigned long)(bo_mem->start << PAGE_SHIFT); if (!ttm->num_pages) { WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n", @@ -547,6 +626,10 @@ static int radeon_ttm_backend_unbind(struct ttm_tt *ttm) struct radeon_ttm_tt *gtt = (void *)ttm; radeon_gart_unbind(gtt->rdev, gtt->offset, ttm->num_pages); + + if (gtt->userptr) + radeon_ttm_tt_unpin_userptr(ttm); + return 0; } @@ -592,10 +675,17 @@ static struct ttm_tt *radeon_ttm_tt_create(struct ttm_bo_device *bdev, return >t->ttm.ttm; } +static struct radeon_ttm_tt *radeon_ttm_tt_to_gtt(struct ttm_tt *ttm) +{ + if (!ttm || ttm->func != &radeon_backend_func) + return NULL; + return (struct radeon_ttm_tt *)ttm; +} + static int radeon_ttm_tt_populate(struct ttm_tt *ttm) { + struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(ttm); struct radeon_device *rdev; - struct radeon_ttm_tt *gtt = (void *)ttm; unsigned i; int r; bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); @@ -603,6 +693,16 @@ static int radeon_ttm_tt_populate(struct ttm_tt *ttm) if (ttm->state != tt_unpopulated) return 0; + if (gtt && gtt->userptr) { + ttm->sg = kcalloc(1, sizeof(struct sg_table), GFP_KERNEL); + if (!ttm->sg) + return -ENOMEM; + + ttm->page_flags |= TTM_PAGE_FLAG_SG; + ttm->state = tt_unbound; + return 0; + } + if (slave && ttm->sg) { drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages, gtt->ttm.dma_address, ttm->num_pages); @@ -648,10 +748,16 @@ static int radeon_ttm_tt_populate(struct ttm_tt *ttm) static void radeon_ttm_tt_unpopulate(struct ttm_tt *ttm) { struct radeon_device *rdev; - struct radeon_ttm_tt *gtt = (void *)ttm; + struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(ttm); unsigned i; bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); + if (gtt && gtt->userptr) { + kfree(ttm->sg); + ttm->page_flags &= ~TTM_PAGE_FLAG_SG; + return; + } + if (slave) return; @@ -680,6 +786,40 @@ static void radeon_ttm_tt_unpopulate(struct ttm_tt *ttm) ttm_pool_unpopulate(ttm); } +int radeon_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr, + uint32_t flags) +{ + struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(ttm); + + if (gtt == NULL) + return -EINVAL; + + gtt->userptr = addr; + gtt->usermm = current->mm; + gtt->userflags = flags; + return 0; +} + +bool radeon_ttm_tt_has_userptr(struct ttm_tt *ttm) +{ + struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(ttm); + + if (gtt == NULL) + return false; + + return !!gtt->userptr; +} + +bool radeon_ttm_tt_is_readonly(struct ttm_tt *ttm) +{ + struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(ttm); + + if (gtt == NULL) + return false; + + return !!(gtt->userflags & RADEON_GEM_USERPTR_READONLY); +} + static struct ttm_bo_driver radeon_bo_driver = { .ttm_tt_create = &radeon_ttm_tt_create, .ttm_tt_populate = &radeon_ttm_tt_populate, @@ -689,11 +829,6 @@ static struct ttm_bo_driver radeon_bo_driver = { .evict_flags = &radeon_evict_flags, .move = &radeon_bo_move, .verify_access = &radeon_verify_access, - .sync_obj_signaled = &radeon_sync_obj_signaled, - .sync_obj_wait = &radeon_sync_obj_wait, - .sync_obj_flush = &radeon_sync_obj_flush, - .sync_obj_unref = &radeon_sync_obj_unref, - .sync_obj_ref = &radeon_sync_obj_ref, .move_notify = &radeon_bo_move_notify, .fault_reserve_notify = &radeon_bo_fault_reserve_notify, .io_mem_reserve = &radeon_ttm_io_mem_reserve, @@ -730,7 +865,7 @@ int radeon_ttm_init(struct radeon_device *rdev) radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size); r = radeon_bo_create(rdev, 256 * 1024, PAGE_SIZE, true, - RADEON_GEM_DOMAIN_VRAM, 0, + RADEON_GEM_DOMAIN_VRAM, 0, NULL, NULL, &rdev->stollen_vga_memory); if (r) { return r; @@ -828,7 +963,7 @@ int radeon_mmap(struct file *filp, struct vm_area_struct *vma) int r; if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) { - return drm_mmap(filp, vma); + return -EINVAL; } file_priv = filp->private_data; diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c index 341848a143761dd8236edc7862f79482d5d2d19d..11b6624692536c13dbea4cc4e9b4c75b46902d1d 100644 --- a/drivers/gpu/drm/radeon/radeon_uvd.c +++ b/drivers/gpu/drm/radeon/radeon_uvd.c @@ -40,12 +40,18 @@ #define UVD_IDLE_TIMEOUT_MS 1000 /* Firmware Names */ +#define FIRMWARE_R600 "radeon/R600_uvd.bin" +#define FIRMWARE_RS780 "radeon/RS780_uvd.bin" +#define FIRMWARE_RV770 "radeon/RV770_uvd.bin" #define FIRMWARE_RV710 "radeon/RV710_uvd.bin" #define FIRMWARE_CYPRESS "radeon/CYPRESS_uvd.bin" #define FIRMWARE_SUMO "radeon/SUMO_uvd.bin" #define FIRMWARE_TAHITI "radeon/TAHITI_uvd.bin" #define FIRMWARE_BONAIRE "radeon/BONAIRE_uvd.bin" +MODULE_FIRMWARE(FIRMWARE_R600); +MODULE_FIRMWARE(FIRMWARE_RS780); +MODULE_FIRMWARE(FIRMWARE_RV770); MODULE_FIRMWARE(FIRMWARE_RV710); MODULE_FIRMWARE(FIRMWARE_CYPRESS); MODULE_FIRMWARE(FIRMWARE_SUMO); @@ -63,6 +69,23 @@ int radeon_uvd_init(struct radeon_device *rdev) INIT_DELAYED_WORK(&rdev->uvd.idle_work, radeon_uvd_idle_work_handler); switch (rdev->family) { + case CHIP_RV610: + case CHIP_RV630: + case CHIP_RV670: + case CHIP_RV620: + case CHIP_RV635: + fw_name = FIRMWARE_R600; + break; + + case CHIP_RS780: + case CHIP_RS880: + fw_name = FIRMWARE_RS780; + break; + + case CHIP_RV770: + fw_name = FIRMWARE_RV770; + break; + case CHIP_RV710: case CHIP_RV730: case CHIP_RV740: @@ -115,9 +138,11 @@ int radeon_uvd_init(struct radeon_device *rdev) } bo_size = RADEON_GPU_PAGE_ALIGN(rdev->uvd_fw->size + 8) + - RADEON_UVD_STACK_SIZE + RADEON_UVD_HEAP_SIZE; + RADEON_UVD_STACK_SIZE + RADEON_UVD_HEAP_SIZE + + RADEON_GPU_PAGE_SIZE; r = radeon_bo_create(rdev, bo_size, PAGE_SIZE, true, - RADEON_GEM_DOMAIN_VRAM, 0, NULL, &rdev->uvd.vcpu_bo); + RADEON_GEM_DOMAIN_VRAM, 0, NULL, + NULL, &rdev->uvd.vcpu_bo); if (r) { dev_err(rdev->dev, "(%d) failed to allocate UVD bo\n", r); return r; @@ -231,10 +256,30 @@ int radeon_uvd_resume(struct radeon_device *rdev) return 0; } -void radeon_uvd_force_into_uvd_segment(struct radeon_bo *rbo) +void radeon_uvd_force_into_uvd_segment(struct radeon_bo *rbo, + uint32_t allowed_domains) { - rbo->placement.fpfn = 0 >> PAGE_SHIFT; - rbo->placement.lpfn = (256 * 1024 * 1024) >> PAGE_SHIFT; + int i; + + for (i = 0; i < rbo->placement.num_placement; ++i) { + rbo->placements[i].fpfn = 0 >> PAGE_SHIFT; + rbo->placements[i].lpfn = (256 * 1024 * 1024) >> PAGE_SHIFT; + } + + /* If it must be in VRAM it must be in the first segment as well */ + if (allowed_domains == RADEON_GEM_DOMAIN_VRAM) + return; + + /* abort if we already have more than one placement */ + if (rbo->placement.num_placement > 1) + return; + + /* add another 256MB segment */ + rbo->placements[1] = rbo->placements[0]; + rbo->placements[1].fpfn += (256 * 1024 * 1024) >> PAGE_SHIFT; + rbo->placements[1].lpfn += (256 * 1024 * 1024) >> PAGE_SHIFT; + rbo->placement.num_placement++; + rbo->placement.num_busy_placement++; } void radeon_uvd_free_handles(struct radeon_device *rdev, struct drm_file *filp) @@ -356,6 +401,7 @@ static int radeon_uvd_cs_msg(struct radeon_cs_parser *p, struct radeon_bo *bo, { int32_t *msg, msg_type, handle; unsigned img_size = 0; + struct fence *f; void *ptr; int i, r; @@ -365,8 +411,9 @@ static int radeon_uvd_cs_msg(struct radeon_cs_parser *p, struct radeon_bo *bo, return -EINVAL; } - if (bo->tbo.sync_obj) { - r = radeon_fence_wait(bo->tbo.sync_obj, false); + f = reservation_object_get_excl(bo->tbo.resv); + if (f) { + r = radeon_fence_wait((struct radeon_fence *)f, false); if (r) { DRM_ERROR("Failed waiting for UVD message (%d)!\n", r); return r; @@ -604,38 +651,16 @@ int radeon_uvd_cs_parse(struct radeon_cs_parser *p) } static int radeon_uvd_send_msg(struct radeon_device *rdev, - int ring, struct radeon_bo *bo, + int ring, uint64_t addr, struct radeon_fence **fence) { - struct ttm_validate_buffer tv; - struct ww_acquire_ctx ticket; - struct list_head head; struct radeon_ib ib; - uint64_t addr; int i, r; - memset(&tv, 0, sizeof(tv)); - tv.bo = &bo->tbo; - - INIT_LIST_HEAD(&head); - list_add(&tv.head, &head); - - r = ttm_eu_reserve_buffers(&ticket, &head); - if (r) - return r; - - radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_VRAM); - radeon_uvd_force_into_uvd_segment(bo); - - r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false); - if (r) - goto err; - r = radeon_ib_get(rdev, ring, &ib, NULL, 64); if (r) - goto err; + return r; - addr = radeon_bo_gpu_offset(bo); ib.ptr[0] = PACKET0(UVD_GPCOM_VCPU_DATA0, 0); ib.ptr[1] = addr; ib.ptr[2] = PACKET0(UVD_GPCOM_VCPU_DATA1, 0); @@ -647,19 +672,11 @@ static int radeon_uvd_send_msg(struct radeon_device *rdev, ib.length_dw = 16; r = radeon_ib_schedule(rdev, &ib, NULL, false); - if (r) - goto err; - ttm_eu_fence_buffer_objects(&ticket, &head, ib.fence); if (fence) *fence = radeon_fence_ref(ib.fence); radeon_ib_free(rdev, &ib); - radeon_bo_unref(&bo); - return 0; - -err: - ttm_eu_backoff_reservation(&ticket, &head); return r; } @@ -669,27 +686,18 @@ static int radeon_uvd_send_msg(struct radeon_device *rdev, int radeon_uvd_get_create_msg(struct radeon_device *rdev, int ring, uint32_t handle, struct radeon_fence **fence) { - struct radeon_bo *bo; - uint32_t *msg; - int r, i; + /* we use the last page of the vcpu bo for the UVD message */ + uint64_t offs = radeon_bo_size(rdev->uvd.vcpu_bo) - + RADEON_GPU_PAGE_SIZE; - r = radeon_bo_create(rdev, 1024, PAGE_SIZE, true, - RADEON_GEM_DOMAIN_VRAM, 0, NULL, &bo); - if (r) - return r; + uint32_t *msg = rdev->uvd.cpu_addr + offs; + uint64_t addr = rdev->uvd.gpu_addr + offs; - r = radeon_bo_reserve(bo, false); - if (r) { - radeon_bo_unref(&bo); - return r; - } + int r, i; - r = radeon_bo_kmap(bo, (void **)&msg); - if (r) { - radeon_bo_unreserve(bo); - radeon_bo_unref(&bo); + r = radeon_bo_reserve(rdev->uvd.vcpu_bo, true); + if (r) return r; - } /* stitch together an UVD create msg */ msg[0] = cpu_to_le32(0x00000de4); @@ -706,36 +714,26 @@ int radeon_uvd_get_create_msg(struct radeon_device *rdev, int ring, for (i = 11; i < 1024; ++i) msg[i] = cpu_to_le32(0x0); - radeon_bo_kunmap(bo); - radeon_bo_unreserve(bo); - - return radeon_uvd_send_msg(rdev, ring, bo, fence); + r = radeon_uvd_send_msg(rdev, ring, addr, fence); + radeon_bo_unreserve(rdev->uvd.vcpu_bo); + return r; } int radeon_uvd_get_destroy_msg(struct radeon_device *rdev, int ring, uint32_t handle, struct radeon_fence **fence) { - struct radeon_bo *bo; - uint32_t *msg; - int r, i; + /* we use the last page of the vcpu bo for the UVD message */ + uint64_t offs = radeon_bo_size(rdev->uvd.vcpu_bo) - + RADEON_GPU_PAGE_SIZE; - r = radeon_bo_create(rdev, 1024, PAGE_SIZE, true, - RADEON_GEM_DOMAIN_VRAM, 0, NULL, &bo); - if (r) - return r; + uint32_t *msg = rdev->uvd.cpu_addr + offs; + uint64_t addr = rdev->uvd.gpu_addr + offs; - r = radeon_bo_reserve(bo, false); - if (r) { - radeon_bo_unref(&bo); - return r; - } + int r, i; - r = radeon_bo_kmap(bo, (void **)&msg); - if (r) { - radeon_bo_unreserve(bo); - radeon_bo_unref(&bo); + r = radeon_bo_reserve(rdev->uvd.vcpu_bo, true); + if (r) return r; - } /* stitch together an UVD destroy msg */ msg[0] = cpu_to_le32(0x00000de4); @@ -745,10 +743,9 @@ int radeon_uvd_get_destroy_msg(struct radeon_device *rdev, int ring, for (i = 4; i < 1024; ++i) msg[i] = cpu_to_le32(0x0); - radeon_bo_kunmap(bo); - radeon_bo_unreserve(bo); - - return radeon_uvd_send_msg(rdev, ring, bo, fence); + r = radeon_uvd_send_msg(rdev, ring, addr, fence); + radeon_bo_unreserve(rdev->uvd.vcpu_bo); + return r; } /** diff --git a/drivers/gpu/drm/radeon/radeon_vce.c b/drivers/gpu/drm/radeon/radeon_vce.c index c7190aadbd8981bee97c0c4ed4d923cdf7880b52..9e85757d55991a6cd0e077cada024d87f0484c08 100644 --- a/drivers/gpu/drm/radeon/radeon_vce.c +++ b/drivers/gpu/drm/radeon/radeon_vce.c @@ -126,7 +126,8 @@ int radeon_vce_init(struct radeon_device *rdev) size = RADEON_GPU_PAGE_ALIGN(rdev->vce_fw->size) + RADEON_VCE_STACK_SIZE + RADEON_VCE_HEAP_SIZE; r = radeon_bo_create(rdev, size, PAGE_SIZE, true, - RADEON_GEM_DOMAIN_VRAM, 0, NULL, &rdev->vce.vcpu_bo); + RADEON_GEM_DOMAIN_VRAM, 0, NULL, NULL, + &rdev->vce.vcpu_bo); if (r) { dev_err(rdev->dev, "(%d) failed to allocate VCE bo\n", r); return r; diff --git a/drivers/gpu/drm/radeon/radeon_vm.c b/drivers/gpu/drm/radeon/radeon_vm.c index 088ffdc2f577c06519e2a31bc90bbd402c940125..4532cc76a0a66094cd1cff8449f8957dd60152bf 100644 --- a/drivers/gpu/drm/radeon/radeon_vm.c +++ b/drivers/gpu/drm/radeon/radeon_vm.c @@ -143,6 +143,7 @@ struct radeon_cs_reloc *radeon_vm_get_bos(struct radeon_device *rdev, list[0].prefered_domains = RADEON_GEM_DOMAIN_VRAM; list[0].allowed_domains = RADEON_GEM_DOMAIN_VRAM; list[0].tv.bo = &vm->page_directory->tbo; + list[0].tv.shared = false; list[0].tiling_flags = 0; list[0].handle = 0; list_add(&list[0].tv.head, head); @@ -156,6 +157,7 @@ struct radeon_cs_reloc *radeon_vm_get_bos(struct radeon_device *rdev, list[idx].prefered_domains = RADEON_GEM_DOMAIN_VRAM; list[idx].allowed_domains = RADEON_GEM_DOMAIN_VRAM; list[idx].tv.bo = &list[idx].robj->tbo; + list[idx].tv.shared = false; list[idx].tiling_flags = 0; list[idx].handle = 0; list_add(&list[idx++].tv.head, head); @@ -395,11 +397,12 @@ static int radeon_vm_clear_bo(struct radeon_device *rdev, memset(&tv, 0, sizeof(tv)); tv.bo = &bo->tbo; + tv.shared = false; INIT_LIST_HEAD(&head); list_add(&tv.head, &head); - r = ttm_eu_reserve_buffers(&ticket, &head); + r = ttm_eu_reserve_buffers(&ticket, &head, true); if (r) return r; @@ -424,7 +427,7 @@ static int radeon_vm_clear_bo(struct radeon_device *rdev, if (r) goto error; - ttm_eu_fence_buffer_objects(&ticket, &head, ib.fence); + ttm_eu_fence_buffer_objects(&ticket, &head, &ib.fence->base); radeon_ib_free(rdev, &ib); return 0; @@ -545,7 +548,8 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev, r = radeon_bo_create(rdev, RADEON_VM_PTE_COUNT * 8, RADEON_GPU_PAGE_SIZE, true, - RADEON_GEM_DOMAIN_VRAM, 0, NULL, &pt); + RADEON_GEM_DOMAIN_VRAM, 0, + NULL, NULL, &pt); if (r) return r; @@ -694,8 +698,9 @@ int radeon_vm_update_page_directory(struct radeon_device *rdev, if (ib.length_dw != 0) { radeon_asic_vm_pad_ib(rdev, &ib); - radeon_semaphore_sync_to(ib.semaphore, pd->tbo.sync_obj); - radeon_semaphore_sync_to(ib.semaphore, vm->last_id_use); + + radeon_semaphore_sync_resv(rdev, ib.semaphore, pd->tbo.resv, false); + radeon_semaphore_sync_fence(ib.semaphore, vm->last_id_use); WARN_ON(ib.length_dw > ndw); r = radeon_ib_schedule(rdev, &ib, NULL, false); if (r) { @@ -821,7 +826,7 @@ static void radeon_vm_update_ptes(struct radeon_device *rdev, unsigned nptes; uint64_t pte; - radeon_semaphore_sync_to(ib->semaphore, pt->tbo.sync_obj); + radeon_semaphore_sync_resv(rdev, ib->semaphore, pt->tbo.resv, false); if ((addr & ~mask) == (end & ~mask)) nptes = end - addr; @@ -892,6 +897,9 @@ int radeon_vm_bo_update(struct radeon_device *rdev, bo_va->flags &= ~RADEON_VM_PAGE_VALID; bo_va->flags &= ~RADEON_VM_PAGE_SYSTEM; bo_va->flags &= ~RADEON_VM_PAGE_SNOOPED; + if (bo_va->bo && radeon_ttm_tt_is_readonly(bo_va->bo->tbo.ttm)) + bo_va->flags &= ~RADEON_VM_PAGE_WRITEABLE; + if (mem) { addr = mem->start << PAGE_SHIFT; if (mem->mem_type != TTM_PL_SYSTEM) { @@ -960,7 +968,7 @@ int radeon_vm_bo_update(struct radeon_device *rdev, radeon_asic_vm_pad_ib(rdev, &ib); WARN_ON(ib.length_dw > ndw); - radeon_semaphore_sync_to(ib.semaphore, vm->fence); + radeon_semaphore_sync_fence(ib.semaphore, vm->fence); r = radeon_ib_schedule(rdev, &ib, NULL, false); if (r) { radeon_ib_free(rdev, &ib); @@ -1120,7 +1128,7 @@ int radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm) r = radeon_bo_create(rdev, pd_size, align, true, RADEON_GEM_DOMAIN_VRAM, 0, NULL, - &vm->page_directory); + NULL, &vm->page_directory); if (r) return r; diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index d9f5ce715c9bfe5ff18be2a428809c1b7b217c58..372016e266d0add52a44b3a9a012d21415d77eea 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -26,7 +26,6 @@ * Jerome Glisse */ #include -#include #include #include #include "radeon.h" diff --git a/drivers/gpu/drm/radeon/rv770_dma.c b/drivers/gpu/drm/radeon/rv770_dma.c index 74426ac2bb5c2f39a26fb07eb76538afcc7061a9..7f34bad2e724d6e657203cfde55f85ddb48fa8da 100644 --- a/drivers/gpu/drm/radeon/rv770_dma.c +++ b/drivers/gpu/drm/radeon/rv770_dma.c @@ -33,18 +33,19 @@ * @src_offset: src GPU address * @dst_offset: dst GPU address * @num_gpu_pages: number of GPU pages to xfer - * @fence: radeon fence object + * @resv: reservation object to sync to * * Copy GPU paging using the DMA engine (r7xx). * Used by the radeon ttm implementation to move pages if * registered as the asic copy callback. */ -int rv770_copy_dma(struct radeon_device *rdev, - uint64_t src_offset, uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence) +struct radeon_fence *rv770_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv) { struct radeon_semaphore *sem = NULL; + struct radeon_fence *fence; int ring_index = rdev->asic->copy.dma_ring_index; struct radeon_ring *ring = &rdev->ring[ring_index]; u32 size_in_dw, cur_size_in_dw; @@ -54,7 +55,7 @@ int rv770_copy_dma(struct radeon_device *rdev, r = radeon_semaphore_create(rdev, &sem); if (r) { DRM_ERROR("radeon: moving bo (%d).\n", r); - return r; + return ERR_PTR(r); } size_in_dw = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT) / 4; @@ -63,10 +64,10 @@ int rv770_copy_dma(struct radeon_device *rdev, if (r) { DRM_ERROR("radeon: moving bo (%d).\n", r); radeon_semaphore_free(rdev, &sem, NULL); - return r; + return ERR_PTR(r); } - radeon_semaphore_sync_to(sem, *fence); + radeon_semaphore_sync_resv(rdev, sem, resv, false); radeon_semaphore_sync_rings(rdev, sem, ring->idx); for (i = 0; i < num_loops; i++) { @@ -83,15 +84,15 @@ int rv770_copy_dma(struct radeon_device *rdev, dst_offset += cur_size_in_dw * 4; } - r = radeon_fence_emit(rdev, fence, ring->idx); + r = radeon_fence_emit(rdev, &fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); radeon_semaphore_free(rdev, &sem, NULL); - return r; + return ERR_PTR(r); } radeon_ring_unlock_commit(rdev, ring, false); - radeon_semaphore_free(rdev, &sem, *fence); + radeon_semaphore_free(rdev, &sem, fence); - return r; + return fence; } diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 3a0b973e8a96ef9f9b9aefb6e39a0459fd03ceff..eeea5b6a1775ee002f36682b7d092ab3b449d913 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -4684,7 +4684,7 @@ static int si_vm_packet3_compute_check(struct radeon_device *rdev, int si_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib) { int ret = 0; - u32 idx = 0; + u32 idx = 0, i; struct radeon_cs_packet pkt; do { @@ -4695,6 +4695,12 @@ int si_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib) switch (pkt.type) { case RADEON_PACKET_TYPE0: dev_err(rdev->dev, "Packet0 not allowed!\n"); + for (i = 0; i < ib->length_dw; i++) { + if (i == idx) + printk("\t0x%08x <---\n", ib->ptr[i]); + else + printk("\t0x%08x\n", ib->ptr[i]); + } ret = -EINVAL; break; case RADEON_PACKET_TYPE2: diff --git a/drivers/gpu/drm/radeon/si_dma.c b/drivers/gpu/drm/radeon/si_dma.c index 7c22baaf94dbf9ac598f745bfb35b2f5ba5138f2..b58f12b762d797c3c4db418712987cf0da43718d 100644 --- a/drivers/gpu/drm/radeon/si_dma.c +++ b/drivers/gpu/drm/radeon/si_dma.c @@ -218,18 +218,19 @@ void si_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm) * @src_offset: src GPU address * @dst_offset: dst GPU address * @num_gpu_pages: number of GPU pages to xfer - * @fence: radeon fence object + * @resv: reservation object to sync to * * Copy GPU paging using the DMA engine (SI). * Used by the radeon ttm implementation to move pages if * registered as the asic copy callback. */ -int si_copy_dma(struct radeon_device *rdev, - uint64_t src_offset, uint64_t dst_offset, - unsigned num_gpu_pages, - struct radeon_fence **fence) +struct radeon_fence *si_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct reservation_object *resv) { struct radeon_semaphore *sem = NULL; + struct radeon_fence *fence; int ring_index = rdev->asic->copy.dma_ring_index; struct radeon_ring *ring = &rdev->ring[ring_index]; u32 size_in_bytes, cur_size_in_bytes; @@ -239,7 +240,7 @@ int si_copy_dma(struct radeon_device *rdev, r = radeon_semaphore_create(rdev, &sem); if (r) { DRM_ERROR("radeon: moving bo (%d).\n", r); - return r; + return ERR_PTR(r); } size_in_bytes = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT); @@ -248,10 +249,10 @@ int si_copy_dma(struct radeon_device *rdev, if (r) { DRM_ERROR("radeon: moving bo (%d).\n", r); radeon_semaphore_free(rdev, &sem, NULL); - return r; + return ERR_PTR(r); } - radeon_semaphore_sync_to(sem, *fence); + radeon_semaphore_sync_resv(rdev, sem, resv, false); radeon_semaphore_sync_rings(rdev, sem, ring->idx); for (i = 0; i < num_loops; i++) { @@ -268,16 +269,16 @@ int si_copy_dma(struct radeon_device *rdev, dst_offset += cur_size_in_bytes; } - r = radeon_fence_emit(rdev, fence, ring->idx); + r = radeon_fence_emit(rdev, &fence, ring->idx); if (r) { radeon_ring_unlock_undo(rdev, ring); radeon_semaphore_free(rdev, &sem, NULL); - return r; + return ERR_PTR(r); } radeon_ring_unlock_commit(rdev, ring, false); - radeon_semaphore_free(rdev, &sem, *fence); + radeon_semaphore_free(rdev, &sem, fence); - return r; + return fence; } diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index 70e61ffeace245aca431cc13c4fa5f833b0580de..9e4d5d7d348f280881f7065862990095e5cee9df 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -2916,7 +2916,6 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, bool disable_sclk_switching = false; u32 mclk, sclk; u16 vddc, vddci; - u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc; int i; if ((rdev->pm.dpm.new_active_crtc_count > 1) || @@ -2950,29 +2949,6 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, } } - /* limit clocks to max supported clocks based on voltage dependency tables */ - btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk, - &max_sclk_vddc); - btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk, - &max_mclk_vddci); - btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk, - &max_mclk_vddc); - - for (i = 0; i < ps->performance_level_count; i++) { - if (max_sclk_vddc) { - if (ps->performance_levels[i].sclk > max_sclk_vddc) - ps->performance_levels[i].sclk = max_sclk_vddc; - } - if (max_mclk_vddci) { - if (ps->performance_levels[i].mclk > max_mclk_vddci) - ps->performance_levels[i].mclk = max_mclk_vddci; - } - if (max_mclk_vddc) { - if (ps->performance_levels[i].mclk > max_mclk_vddc) - ps->performance_levels[i].mclk = max_mclk_vddc; - } - } - /* XXX validate the min clocks required for display */ if (disable_mclk_switching) { diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h index fd414d34d885791ce8b85d303249805eb061ecbe..6635da9ec986f9b4eadfa50021f4f35cafe25a61 100644 --- a/drivers/gpu/drm/radeon/sid.h +++ b/drivers/gpu/drm/radeon/sid.h @@ -736,7 +736,7 @@ # define DESCRIPTION16(x) (((x) & 0xff) << 0) # define DESCRIPTION17(x) (((x) & 0xff) << 8) -#define AZ_F0_CODEC_PIN_CONTROL_HOTPLUG_CONTROL 0x54 +#define AZ_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL 0x54 # define AUDIO_ENABLED (1 << 31) #define AZ_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT 0x56 diff --git a/drivers/gpu/drm/radeon/uvd_v1_0.c b/drivers/gpu/drm/radeon/uvd_v1_0.c index cda391347286c3545bdd952234b3b5fb7186a5d9..e72b3cb593589876cc8999e8ab913cbf886b64f3 100644 --- a/drivers/gpu/drm/radeon/uvd_v1_0.c +++ b/drivers/gpu/drm/radeon/uvd_v1_0.c @@ -22,6 +22,7 @@ * Authors: Christian König */ +#include #include #include "radeon.h" #include "radeon_asic.h" @@ -69,6 +70,82 @@ void uvd_v1_0_set_wptr(struct radeon_device *rdev, WREG32(UVD_RBC_RB_WPTR, ring->wptr); } +/** + * uvd_v1_0_fence_emit - emit an fence & trap command + * + * @rdev: radeon_device pointer + * @fence: fence to emit + * + * Write a fence and a trap command to the ring. + */ +void uvd_v1_0_fence_emit(struct radeon_device *rdev, + struct radeon_fence *fence) +{ + struct radeon_ring *ring = &rdev->ring[fence->ring]; + uint64_t addr = rdev->fence_drv[fence->ring].gpu_addr; + + radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA0, 0)); + radeon_ring_write(ring, addr & 0xffffffff); + radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA1, 0)); + radeon_ring_write(ring, fence->seq); + radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_CMD, 0)); + radeon_ring_write(ring, 0); + + radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA0, 0)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA1, 0)); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_CMD, 0)); + radeon_ring_write(ring, 2); + return; +} + +/** + * uvd_v1_0_resume - memory controller programming + * + * @rdev: radeon_device pointer + * + * Let the UVD memory controller know it's offsets + */ +int uvd_v1_0_resume(struct radeon_device *rdev) +{ + uint64_t addr; + uint32_t size; + int r; + + r = radeon_uvd_resume(rdev); + if (r) + return r; + + /* programm the VCPU memory controller bits 0-27 */ + addr = (rdev->uvd.gpu_addr >> 3) + 16; + size = RADEON_GPU_PAGE_ALIGN(rdev->uvd_fw->size) >> 3; + WREG32(UVD_VCPU_CACHE_OFFSET0, addr); + WREG32(UVD_VCPU_CACHE_SIZE0, size); + + addr += size; + size = RADEON_UVD_STACK_SIZE >> 3; + WREG32(UVD_VCPU_CACHE_OFFSET1, addr); + WREG32(UVD_VCPU_CACHE_SIZE1, size); + + addr += size; + size = RADEON_UVD_HEAP_SIZE >> 3; + WREG32(UVD_VCPU_CACHE_OFFSET2, addr); + WREG32(UVD_VCPU_CACHE_SIZE2, size); + + /* bits 28-31 */ + addr = (rdev->uvd.gpu_addr >> 28) & 0xF; + WREG32(UVD_LMI_ADDR_EXT, (addr << 12) | (addr << 0)); + + /* bits 32-39 */ + addr = (rdev->uvd.gpu_addr >> 32) & 0xFF; + WREG32(UVD_LMI_EXT40_ADDR, addr | (0x9 << 16) | (0x1 << 31)); + + WREG32(UVD_FW_START, *((uint32_t*)rdev->uvd.cpu_addr)); + + return 0; +} + /** * uvd_v1_0_init - start and test UVD block * @@ -130,8 +207,32 @@ int uvd_v1_0_init(struct radeon_device *rdev) /* lower clocks again */ radeon_set_uvd_clocks(rdev, 0, 0); - if (!r) + if (!r) { + switch (rdev->family) { + case CHIP_RV610: + case CHIP_RV630: + case CHIP_RV620: + /* 64byte granularity workaround */ + WREG32(MC_CONFIG, 0); + WREG32(MC_CONFIG, 1 << 4); + WREG32(RS_DQ_RD_RET_CONF, 0x3f); + WREG32(MC_CONFIG, 0x1f); + + /* fall through */ + case CHIP_RV670: + case CHIP_RV635: + + /* write clean workaround */ + WREG32_P(UVD_VCPU_CNTL, 0x10, ~0x10); + break; + + default: + /* TODO: Do we need more? */ + break; + } + DRM_INFO("UVD initialized successfully.\n"); + } return r; } @@ -218,12 +319,12 @@ int uvd_v1_0_start(struct radeon_device *rdev) /* enable UMC */ WREG32_P(UVD_LMI_CTRL2, 0, ~(1 << 8)); + WREG32_P(UVD_RB_ARB_CTRL, 0, ~(1 << 3)); + /* boot up the VCPU */ WREG32(UVD_SOFT_RESET, 0); mdelay(10); - WREG32_P(UVD_RB_ARB_CTRL, 0, ~(1 << 3)); - for (i = 0; i < 10; ++i) { uint32_t status; for (j = 0; j < 100; ++j) { diff --git a/drivers/gpu/drm/radeon/uvd_v2_2.c b/drivers/gpu/drm/radeon/uvd_v2_2.c index 8bfdadd5659881d6a47b10ff888592c96417ebee..89193519f8a1faf32fd7fe9013ddd8107cafd977 100644 --- a/drivers/gpu/drm/radeon/uvd_v2_2.c +++ b/drivers/gpu/drm/radeon/uvd_v2_2.c @@ -72,6 +72,10 @@ int uvd_v2_2_resume(struct radeon_device *rdev) uint32_t chip_id, size; int r; + /* RV770 uses V1.0 MC */ + if (rdev->family == CHIP_RV770) + return uvd_v1_0_resume(rdev); + r = radeon_uvd_resume(rdev); if (r) return r; diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig index 2e3d7b5b0ad7d8363c11c7f7942858a88b03f4ed..c96f6089f8bf195377a933a8b1632565ebe49930 100644 --- a/drivers/gpu/drm/rcar-du/Kconfig +++ b/drivers/gpu/drm/rcar-du/Kconfig @@ -6,6 +6,7 @@ config DRM_RCAR_DU select DRM_KMS_CMA_HELPER select DRM_GEM_CMA_HELPER select DRM_KMS_FB_HELPER + select VIDEOMODE_HELPERS help Choose this option if you have an R-Car chipset. If M is selected the module will be called rcar-du-drm. diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 299267db2898634fae3830c7be1180b1188ec737..148b505891813f65dee9019e24741fd003c4b41c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -1,7 +1,7 @@ /* * rcar_du_crtc.c -- R-Car Display Unit CRTCs * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index 43e7575c700c26354d9971ea211ef84a4db2cfc9..e97ae502dec5ffaa50311d84f96d66b17e69ea8b 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h @@ -1,7 +1,7 @@ /* * rcar_du_crtc.h -- R-Car Display Unit CRTCs * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index fda64b7b73e816f80716f0a33b2764a7b4711af1..d212efa6a49510db6b51f60a77357e5b6c5e09a9 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -1,7 +1,7 @@ /* * rcar_du_drv.c -- R-Car Display Unit DRM driver * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,97 @@ #include "rcar_du_kms.h" #include "rcar_du_regs.h" +/* ----------------------------------------------------------------------------- + * Device Information + */ + +static const struct rcar_du_device_info rcar_du_r8a7779_info = { + .features = 0, + .num_crtcs = 2, + .routes = { + /* R8A7779 has two RGB outputs and one (currently unsupported) + * TCON output. + */ + [RCAR_DU_OUTPUT_DPAD0] = { + .possible_crtcs = BIT(0), + .encoder_type = DRM_MODE_ENCODER_NONE, + .port = 0, + }, + [RCAR_DU_OUTPUT_DPAD1] = { + .possible_crtcs = BIT(1) | BIT(0), + .encoder_type = DRM_MODE_ENCODER_NONE, + .port = 1, + }, + }, + .num_lvds = 0, +}; + +static const struct rcar_du_device_info rcar_du_r8a7790_info = { + .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_DEFR8, + .quirks = RCAR_DU_QUIRK_ALIGN_128B | RCAR_DU_QUIRK_LVDS_LANES, + .num_crtcs = 3, + .routes = { + /* R8A7790 has one RGB output, two LVDS outputs and one + * (currently unsupported) TCON output. + */ + [RCAR_DU_OUTPUT_DPAD0] = { + .possible_crtcs = BIT(2) | BIT(1) | BIT(0), + .encoder_type = DRM_MODE_ENCODER_NONE, + .port = 0, + }, + [RCAR_DU_OUTPUT_LVDS0] = { + .possible_crtcs = BIT(0), + .encoder_type = DRM_MODE_ENCODER_LVDS, + .port = 1, + }, + [RCAR_DU_OUTPUT_LVDS1] = { + .possible_crtcs = BIT(2) | BIT(1), + .encoder_type = DRM_MODE_ENCODER_LVDS, + .port = 2, + }, + }, + .num_lvds = 2, +}; + +static const struct rcar_du_device_info rcar_du_r8a7791_info = { + .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_DEFR8, + .num_crtcs = 2, + .routes = { + /* R8A7791 has one RGB output, one LVDS output and one + * (currently unsupported) TCON output. + */ + [RCAR_DU_OUTPUT_DPAD0] = { + .possible_crtcs = BIT(1), + .encoder_type = DRM_MODE_ENCODER_NONE, + .port = 0, + }, + [RCAR_DU_OUTPUT_LVDS0] = { + .possible_crtcs = BIT(0), + .encoder_type = DRM_MODE_ENCODER_LVDS, + .port = 1, + }, + }, + .num_lvds = 1, +}; + +static const struct platform_device_id rcar_du_id_table[] = { + { "rcar-du-r8a7779", (kernel_ulong_t)&rcar_du_r8a7779_info }, + { "rcar-du-r8a7790", (kernel_ulong_t)&rcar_du_r8a7790_info }, + { "rcar-du-r8a7791", (kernel_ulong_t)&rcar_du_r8a7791_info }, + { } +}; + +MODULE_DEVICE_TABLE(platform, rcar_du_id_table); + +static const struct of_device_id rcar_du_of_table[] = { + { .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info }, + { .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info }, + { .compatible = "renesas,du-r8a7791", .data = &rcar_du_r8a7791_info }, + { } +}; + +MODULE_DEVICE_TABLE(of, rcar_du_of_table); + /* ----------------------------------------------------------------------------- * DRM operations */ @@ -53,12 +145,13 @@ static int rcar_du_unload(struct drm_device *dev) static int rcar_du_load(struct drm_device *dev, unsigned long flags) { struct platform_device *pdev = dev->platformdev; + struct device_node *np = pdev->dev.of_node; struct rcar_du_platform_data *pdata = pdev->dev.platform_data; struct rcar_du_device *rcdu; struct resource *mem; int ret; - if (pdata == NULL) { + if (pdata == NULL && np == NULL) { dev_err(dev->dev, "no platform data\n"); return -ENODEV; } @@ -71,7 +164,8 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags) rcdu->dev = &pdev->dev; rcdu->pdata = pdata; - rcdu->info = (struct rcar_du_device_info *)pdev->id_entry->driver_data; + rcdu->info = np ? of_match_device(rcar_du_of_table, rcdu->dev)->data + : (void *)platform_get_device_id(pdev)->driver_data; rcdu->ddev = dev; dev->dev_private = rcdu; @@ -158,6 +252,7 @@ static struct drm_driver rcar_du_driver = { .unload = rcar_du_unload, .preclose = rcar_du_preclose, .lastclose = rcar_du_lastclose, + .set_busid = drm_platform_set_busid, .get_vblank_counter = drm_vblank_count, .enable_vblank = rcar_du_enable_vblank, .disable_vblank = rcar_du_disable_vblank, @@ -231,77 +326,6 @@ static int rcar_du_remove(struct platform_device *pdev) return 0; } -static const struct rcar_du_device_info rcar_du_r8a7779_info = { - .features = 0, - .num_crtcs = 2, - .routes = { - /* R8A7779 has two RGB outputs and one (currently unsupported) - * TCON output. - */ - [RCAR_DU_OUTPUT_DPAD0] = { - .possible_crtcs = BIT(0), - .encoder_type = DRM_MODE_ENCODER_NONE, - }, - [RCAR_DU_OUTPUT_DPAD1] = { - .possible_crtcs = BIT(1) | BIT(0), - .encoder_type = DRM_MODE_ENCODER_NONE, - }, - }, - .num_lvds = 0, -}; - -static const struct rcar_du_device_info rcar_du_r8a7790_info = { - .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_DEFR8, - .quirks = RCAR_DU_QUIRK_ALIGN_128B | RCAR_DU_QUIRK_LVDS_LANES, - .num_crtcs = 3, - .routes = { - /* R8A7790 has one RGB output, two LVDS outputs and one - * (currently unsupported) TCON output. - */ - [RCAR_DU_OUTPUT_DPAD0] = { - .possible_crtcs = BIT(2) | BIT(1) | BIT(0), - .encoder_type = DRM_MODE_ENCODER_NONE, - }, - [RCAR_DU_OUTPUT_LVDS0] = { - .possible_crtcs = BIT(0), - .encoder_type = DRM_MODE_ENCODER_LVDS, - }, - [RCAR_DU_OUTPUT_LVDS1] = { - .possible_crtcs = BIT(2) | BIT(1), - .encoder_type = DRM_MODE_ENCODER_LVDS, - }, - }, - .num_lvds = 2, -}; - -static const struct rcar_du_device_info rcar_du_r8a7791_info = { - .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_DEFR8, - .num_crtcs = 2, - .routes = { - /* R8A7791 has one RGB output, one LVDS output and one - * (currently unsupported) TCON output. - */ - [RCAR_DU_OUTPUT_DPAD0] = { - .possible_crtcs = BIT(1), - .encoder_type = DRM_MODE_ENCODER_NONE, - }, - [RCAR_DU_OUTPUT_LVDS0] = { - .possible_crtcs = BIT(0), - .encoder_type = DRM_MODE_ENCODER_LVDS, - }, - }, - .num_lvds = 1, -}; - -static const struct platform_device_id rcar_du_id_table[] = { - { "rcar-du-r8a7779", (kernel_ulong_t)&rcar_du_r8a7779_info }, - { "rcar-du-r8a7790", (kernel_ulong_t)&rcar_du_r8a7790_info }, - { "rcar-du-r8a7791", (kernel_ulong_t)&rcar_du_r8a7791_info }, - { } -}; - -MODULE_DEVICE_TABLE(platform, rcar_du_id_table); - static struct platform_driver rcar_du_platform_driver = { .probe = rcar_du_probe, .remove = rcar_du_remove, @@ -309,6 +333,7 @@ static struct platform_driver rcar_du_platform_driver = { .owner = THIS_MODULE, .name = "rcar-du", .pm = &rcar_du_pm_ops, + .of_match_table = rcar_du_of_table, }, .id_table = rcar_du_id_table, }; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index e31b735d3f258b69b65ab97320ddfe7440d7d6b9..8e494633c3b36325b99833dfd7ed294cab975962 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -1,7 +1,7 @@ /* * rcar_du_drv.h -- R-Car Display Unit DRM driver * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -37,6 +37,7 @@ struct rcar_du_lvdsenc; * struct rcar_du_output_routing - Output routing specification * @possible_crtcs: bitmask of possible CRTCs for the output * @encoder_type: DRM type of the internal encoder associated with the output + * @port: device tree port number corresponding to this output route * * The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). Output routing data * specify the valid SoC outputs, which CRTCs can drive the output, and the type @@ -45,6 +46,7 @@ struct rcar_du_lvdsenc; struct rcar_du_output_routing { unsigned int possible_crtcs; unsigned int encoder_type; + unsigned int port; }; /* diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 3daa7a168dc689dba73804863998c5c1de4946ca..7c0ec95915eff1b1a46fb0319f5ae7eea353c26a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -1,7 +1,7 @@ /* * rcar_du_encoder.c -- R-Car Display Unit Encoder * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -142,7 +142,8 @@ static const struct drm_encoder_funcs encoder_funcs = { int rcar_du_encoder_init(struct rcar_du_device *rcdu, enum rcar_du_encoder_type type, enum rcar_du_output output, - const struct rcar_du_encoder_data *data) + const struct rcar_du_encoder_data *data, + struct device_node *np) { struct rcar_du_encoder *renc; unsigned int encoder_type; @@ -189,9 +190,11 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs); switch (encoder_type) { - case DRM_MODE_ENCODER_LVDS: - return rcar_du_lvds_connector_init(rcdu, renc, - &data->connector.lvds.panel); + case DRM_MODE_ENCODER_LVDS: { + const struct rcar_du_panel_data *pdata = + data ? &data->connector.lvds.panel : NULL; + return rcar_du_lvds_connector_init(rcdu, renc, pdata, np); + } case DRM_MODE_ENCODER_DAC: return rcar_du_vga_connector_init(rcdu, renc); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h index 0e5a65e45d0e598d338399b838d1e60d2291f97e..bd624135ef1fbfed3257bc4377b1e17a441b2021 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h @@ -1,7 +1,7 @@ /* * rcar_du_encoder.h -- R-Car Display Unit Encoder * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -44,6 +44,7 @@ rcar_du_connector_best_encoder(struct drm_connector *connector); int rcar_du_encoder_init(struct rcar_du_device *rcdu, enum rcar_du_encoder_type type, enum rcar_du_output output, - const struct rcar_du_encoder_data *data); + const struct rcar_du_encoder_data *data, + struct device_node *np); #endif /* __RCAR_DU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c index eb53cd97e8c6c3ac282bd5ccd09cd8a637275026..4e7614b145db550d1364723e76dd27ffd36b404f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c @@ -1,7 +1,7 @@ /* * rcar_du_group.c -- R-Car Display Unit Channels Pair * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.h b/drivers/gpu/drm/rcar-du/rcar_du_group.h index 5025930972ecdd36c2eb652e2931dcfff6788651..0c38cdcda4cae9a0880bf354b7ffd167c16be870 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.h @@ -1,7 +1,7 @@ /* * rcar_du_group.c -- R-Car Display Unit Planes and CRTCs Group * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 76026104d000170838fd5bf34361d30a8b1efa52..6c24ad7d03ef300077cc992b9d3b95fccec2cac1 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -1,7 +1,7 @@ /* * rcar_du_kms.c -- R-Car Display Unit Mode Setting * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -17,6 +17,8 @@ #include #include +#include + #include "rcar_du_crtc.h" #include "rcar_du_drv.h" #include "rcar_du_encoder.h" @@ -188,6 +190,205 @@ static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = { .output_poll_changed = rcar_du_output_poll_changed, }; +static int rcar_du_encoders_init_pdata(struct rcar_du_device *rcdu) +{ + unsigned int num_encoders = 0; + unsigned int i; + int ret; + + for (i = 0; i < rcdu->pdata->num_encoders; ++i) { + const struct rcar_du_encoder_data *pdata = + &rcdu->pdata->encoders[i]; + const struct rcar_du_output_routing *route = + &rcdu->info->routes[pdata->output]; + + if (pdata->type == RCAR_DU_ENCODER_UNUSED) + continue; + + if (pdata->output >= RCAR_DU_OUTPUT_MAX || + route->possible_crtcs == 0) { + dev_warn(rcdu->dev, + "encoder %u references unexisting output %u, skipping\n", + i, pdata->output); + continue; + } + + ret = rcar_du_encoder_init(rcdu, pdata->type, pdata->output, + pdata, NULL); + if (ret < 0) + return ret; + + num_encoders++; + } + + return num_encoders; +} + +static int rcar_du_encoders_init_dt_one(struct rcar_du_device *rcdu, + enum rcar_du_output output, + struct of_endpoint *ep) +{ + static const struct { + const char *compatible; + enum rcar_du_encoder_type type; + } encoders[] = { + { "adi,adv7123", RCAR_DU_ENCODER_VGA }, + { "thine,thc63lvdm83d", RCAR_DU_ENCODER_LVDS }, + }; + + enum rcar_du_encoder_type enc_type = RCAR_DU_ENCODER_NONE; + struct device_node *connector = NULL; + struct device_node *encoder = NULL; + struct device_node *prev = NULL; + struct device_node *entity_ep_node; + struct device_node *entity; + int ret; + + /* + * Locate the connected entity and infer its type from the number of + * endpoints. + */ + entity = of_graph_get_remote_port_parent(ep->local_node); + if (!entity) { + dev_dbg(rcdu->dev, "unconnected endpoint %s, skipping\n", + ep->local_node->full_name); + return 0; + } + + entity_ep_node = of_parse_phandle(ep->local_node, "remote-endpoint", 0); + + while (1) { + struct device_node *ep_node; + + ep_node = of_graph_get_next_endpoint(entity, prev); + of_node_put(prev); + prev = ep_node; + + if (!ep_node) + break; + + if (ep_node == entity_ep_node) + continue; + + /* + * We've found one endpoint other than the input, this must + * be an encoder. Locate the connector. + */ + encoder = entity; + connector = of_graph_get_remote_port_parent(ep_node); + of_node_put(ep_node); + + if (!connector) { + dev_warn(rcdu->dev, + "no connector for encoder %s, skipping\n", + encoder->full_name); + of_node_put(entity_ep_node); + of_node_put(encoder); + return 0; + } + + break; + } + + of_node_put(entity_ep_node); + + if (encoder) { + /* + * If an encoder has been found, get its type based on its + * compatible string. + */ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(encoders); ++i) { + if (of_device_is_compatible(encoder, + encoders[i].compatible)) { + enc_type = encoders[i].type; + break; + } + } + + if (i == ARRAY_SIZE(encoders)) { + dev_warn(rcdu->dev, + "unknown encoder type for %s, skipping\n", + encoder->full_name); + of_node_put(encoder); + of_node_put(connector); + return 0; + } + } else { + /* + * If no encoder has been found the entity must be the + * connector. + */ + connector = entity; + } + + ret = rcar_du_encoder_init(rcdu, enc_type, output, NULL, connector); + of_node_put(encoder); + of_node_put(connector); + + return ret < 0 ? ret : 1; +} + +static int rcar_du_encoders_init_dt(struct rcar_du_device *rcdu) +{ + struct device_node *np = rcdu->dev->of_node; + struct device_node *prev = NULL; + unsigned int num_encoders = 0; + + /* + * Iterate over the endpoints and create one encoder for each output + * pipeline. + */ + while (1) { + struct device_node *ep_node; + enum rcar_du_output output; + struct of_endpoint ep; + unsigned int i; + int ret; + + ep_node = of_graph_get_next_endpoint(np, prev); + of_node_put(prev); + prev = ep_node; + + if (ep_node == NULL) + break; + + ret = of_graph_parse_endpoint(ep_node, &ep); + if (ret < 0) { + of_node_put(ep_node); + return ret; + } + + /* Find the output route corresponding to the port number. */ + for (i = 0; i < RCAR_DU_OUTPUT_MAX; ++i) { + if (rcdu->info->routes[i].possible_crtcs && + rcdu->info->routes[i].port == ep.port) { + output = i; + break; + } + } + + if (i == RCAR_DU_OUTPUT_MAX) { + dev_warn(rcdu->dev, + "port %u references unexisting output, skipping\n", + ep.port); + continue; + } + + /* Process the output pipeline. */ + ret = rcar_du_encoders_init_dt_one(rcdu, output, &ep); + if (ret < 0) { + of_node_put(ep_node); + return ret; + } + + num_encoders += ret; + } + + return num_encoders; +} + int rcar_du_modeset_init(struct rcar_du_device *rcdu) { static const unsigned int mmio_offsets[] = { @@ -197,6 +398,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) struct drm_device *dev = rcdu->ddev; struct drm_encoder *encoder; struct drm_fbdev_cma *fbdev; + unsigned int num_encoders; unsigned int num_groups; unsigned int i; int ret; @@ -240,28 +442,15 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) if (ret < 0) return ret; - for (i = 0; i < rcdu->pdata->num_encoders; ++i) { - const struct rcar_du_encoder_data *pdata = - &rcdu->pdata->encoders[i]; - const struct rcar_du_output_routing *route = - &rcdu->info->routes[pdata->output]; - - if (pdata->type == RCAR_DU_ENCODER_UNUSED) - continue; + if (rcdu->pdata) + ret = rcar_du_encoders_init_pdata(rcdu); + else + ret = rcar_du_encoders_init_dt(rcdu); - if (pdata->output >= RCAR_DU_OUTPUT_MAX || - route->possible_crtcs == 0) { - dev_warn(rcdu->dev, - "encoder %u references unexisting output %u, skipping\n", - i, pdata->output); - continue; - } + if (ret < 0) + return ret; - ret = rcar_du_encoder_init(rcdu, pdata->type, pdata->output, - pdata); - if (ret < 0) - return ret; - } + num_encoders = ret; /* Set the possible CRTCs and possible clones. There's always at least * one way for all encoders to clone each other, set all bits in the @@ -273,7 +462,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) &rcdu->info->routes[renc->output]; encoder->possible_crtcs = route->possible_crtcs; - encoder->possible_clones = (1 << rcdu->pdata->num_encoders) - 1; + encoder->possible_clones = (1 << num_encoders) - 1; } /* Now that the CRTCs have been initialized register the planes. */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/rcar-du/rcar_du_kms.h index 5750e6af56557b36b4821953fced65372170fa09..07951d5fe38b92d5b5256a8166dde2498cd1a98a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.h @@ -1,7 +1,7 @@ /* * rcar_du_kms.h -- R-Car Display Unit Mode Setting * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c index 21426bd234eb1d24ac2038e8ed58fd813ac61990..115eed20db12abc421eddf7a20db0707c291c4a5 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c @@ -1,7 +1,7 @@ /* * rcar_du_lvdscon.c -- R-Car Display Unit LVDS Connector * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -15,6 +15,10 @@ #include #include +#include